Программно получать время загрузки системы в С++ (windows)

Так просто, вопрос в том, как получить время загрузки системы в Windows с помощью c/С++.

Поиск этого не получил от меня ответа, я только нашел очень хакерский подход, который читает временную метку файла (само собой разумеется, я отказался прочитать это на полпути).

Другой подход, который я нашел, - это действительно чтение журнальных событий Windows? Предположительно, последний раз загрузился.

Кто-нибудь знает, как это сделать (надеюсь, не слишком много уродливых хаков)?

Ответ 1

GetTickCount64 "извлекает количество миллисекунд, прошедших с момента запуска системы."

Как только вы знаете, как долго система работает, это просто вопрос вычитания этой продолжительности с текущего времени, чтобы определить, когда она была загружена. Например, используя библиотеку chrono С++ 11 (поддерживаемую Visual С++ 2012):

auto uptime = std::chrono::milliseconds(GetTickCount64());
auto boot_time = std::chrono::system_clock::now() - uptime;

Ответ 2

Вы также можете использовать WMI, чтобы получить точное время загрузки. WMI не для слабонервных, но он даст вам то, что вы ищете.

Данная информация относится к объекту Win32_OperatingSystem под свойством LastBootUpTime. Вы можете изучить другие свойства, используя WMI Tools.

WMI Explorer showing property instance

Edit: Вы также можете получить эту информацию из командной строки, если хотите.

wmic OS Get LastBootUpTime

В качестве примера в С# он будет выглядеть следующим образом (Использование С++ довольно многословно):

static void Main(string[] args)
{      
    // Create a query for OS objects
    SelectQuery query = new SelectQuery("Win32_OperatingSystem", "Status=\"OK\"");

    // Initialize an object searcher with this query
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

    string dtString;
    // Get the resulting collection and loop through it
    foreach (ManagementObject envVar in searcher.Get())
        dtString = envVar["LastBootUpTime"].ToString();
}

Ответ 3

C++ Boost использовал WMI LastBootUpTime но в версии 1.54 переключился на проверку журнала системных событий, и, очевидно, по уважительной причине:

Прерывание ABI: изменена функция начальной загрузки в Windows, чтобы использовать время запуска службы EventLog в качестве времени загрузки системы. Ранее использованный LastBootupTime из WMI был нестабилен с синхронизацией времени и гибернацией и на практике непригоден. Если вам действительно нужно получить поведение до Boost 1.54, определите BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME из командной строки или detail/workaround.hpp.

Посмотрите на boost/interprocess/detail/win32_api.hpp, около строки 2201, реализацию inline bool get_last_bootup_time(std::string &stamp) функции inline bool get_last_bootup_time(std::string &stamp) для примера. (Я смотрю на версию 1.60, если вы хотите сопоставить номера строк.)

На всякий случай, если Boost когда-нибудь умрет, и мое указание на Boost не поможет (да, верно), функция, которую вы хотите, в основном - ReadEventLogA и идентификатор события, который нужно искать ("Event Log Started" согласно комментариям Boost), видимо 6005.

Ответ 4

Не отдельный ответ, но я реализовал каждый из трех других ответов и у меня есть некоторые наблюдения, которые могут помочь тем, кто пытается выбрать метод.

Использование GetTickCount64 и вычитание из текущего времени

  • Самый быстрый метод с тактовой частотой 0,112 мс.
  • Не выдает уникальное/непротиворечивое значение при разрешении своих аргументов за 100 нс, поскольку оно зависит от тактов. Все возвращаемые значения находятся в пределах 1/64 секунды друг от друга.
  • Требуется Vista или новее. 32-разрядный счетчик XP переносится на ~ 49 дней и не может использоваться для этого подхода, если ваше приложение/библиотека должно поддерживать более старые версии Windows

Использование запроса WMI из LastBootUpTime области Win32_OperatingSystem

  • Потребовалось 84 мс с использованием COM, 202 мс с использованием командной строки wmic.
  • CIM_DATETIME согласованное значение в виде строки CIM_DATETIME
  • Класс WMI требует Vista или новее.

Чтение журнала событий

  • Самый медленный метод, занимающий 229 мс
  • Создает согласованное значение в единицах секунд (время Unix)
  • Работает на Windows 2000 или новее.

Методы также производили разные временные метки:

  • UpTime: 1558758098843 = 2019-05-25 04:21:38 UTC (иногда: 37)
  • WMI: 20190524222528.665400-420 = 2019-05-25 05:25:28 UTC
  • Журнал событий: 1558693023 = 2019-05-24 10:17:03 UTC

Вывод: метод журнала событий совместим со старыми версиями Windows, выдает согласованную временную метку во времени Unix, которая не зависит от циклов сна/спящего режима, но также является самой медленной. Учитывая, что это вряд ли будет выполнено в цикле, это, вероятно, лучший вариант.