以c ++(windows)编程方式获取系统启动时间

时间:2012-06-01 16:22:23

标签: c++ c windows

非常简单,问题是如何使用c / c ++在Windows中启动系统启动时间。

搜索这个没有得到任何答案,我只找到了一个非常hacky的方法,即读取文件时间戳(不用说,我放弃了中途阅读)。

我发现的另一种方法实际上是读取Windows诊断记录的事件?据说最后一次启动时间。

有谁知道如何做到这一点(希望没有太多丑陋的黑客)?

5 个答案:

答案 0 :(得分:18)

GetTickCount64“检索自系统启动以来经过的毫秒数。”

一旦您知道系统运行了多长时间,只需从当前时间减去此持续时间即可确定系统何时启动。例如,使用C ++ 11 chrono库(Visual C ++ 2012支持):

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

答案 1 :(得分:6)

您还可以使用WMI来获取准确的启动时间。 WMI不适合胆小的人,但它会让你得到你想要的东西。

相关信息位于LastBootUpTime属性下的Win32_OperatingSystem对象上。您可以使用WMI Tools检查其他属性。

WMI Explorer showing property instance

修改 如果您愿意,也可以从命令行获取此信息。

wmic OS Get LastBootUpTime

作为C#中的示例,它看起来像以下(Using C++ it is rather verbose):

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();
}

答案 2 :(得分:1)

我还没有做太多的事情,但是我个人认为最好的方法可能是查询“系统”进程的开始时间。在Windows上,内核会出于自身目的在启动时分配一个进程(令人惊讶的是,尽管我确定信息已经存在,但快速的Google搜索并不能轻易揭示其实际用途。)此过程在任务管理器中简称为“系统”,在当前Windows版本上始终具有PID 4(显然NT 4和Windows 2000可能已使用PID 8)。只要系统正在运行,此过程就永远不会退出,就我的测试而言,就元数据而言,它的行为就像一个成熟的过程。根据我的测试,看起来即使是没有提升权限的用户也可以打开PID 4的句柄,请求PROCESS_QUERY_LIMITED_INFORMATION,并且生成的句柄可以与GetProcessTimes一起使用,它将填充{{1} },以及流程开始时间的UTC时间戳。据我所知,在系统进程运行之前,Windows的运行方式没有任何有意义的方式,因此此时间戳几乎完全是Windows启动时的时间。

lpCreationTime

当前启动的比较:

  • #include <iostream> #include <iomanip> #include <windows.h> using namespace std; int main() { unique_ptr<remove_pointer<HANDLE>::type, decltype(&::CloseHandle)> hProcess( ::OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, // bInheritHandle 4), // dwProcessId ::CloseHandle); FILETIME creationTimeStamp, exitTimeStamp, kernelTimeUsed, userTimeUsed; FILETIME creationTimeStampLocal; SYSTEMTIME creationTimeStampSystem; if (::GetProcessTimes(hProcess.get(), &creationTimeStamp, &exitTimeStamp, &kernelTimeUsed, &userTimeUsed) && ::FileTimeToLocalFileTime(&creationTimeStamp, &creationTimeStampLocal) && ::FileTimeToSystemTime(&creationTimeStampLocal, &creationTimeStampSystem)) { __int64 ticks = ((__int64)creationTimeStampLocal.dwHighDateTime) << 32 | creationTimeStampLocal.dwLowDateTime; wios saved(NULL); saved.copyfmt(wcout); wcout << setfill(L'0') << setw(4) << creationTimeStampSystem.wYear << L'-' << setw(2) << creationTimeStampSystem.wMonth << L'-' << creationTimeStampSystem.wDay << L' ' << creationTimeStampSystem.wHour << L':' << creationTimeStampSystem.wMinute << L':' << creationTimeStampSystem.wSecond << L'.' << setw(7) << (ticks % 10000000) << endl; wcout.copyfmt(saved); } }

2020-07-18 17:36:41.3284297
2020-07-18 17:36:41.3209437
2020-07-18 17:36:41.3134106
2020-07-18 17:36:41.3225148
2020-07-18 17:36:41.3145312

(结果因呼叫而异,因为system_clock::now() - milliseconds(GetTickCount64())system_clock::now()的运行时间不完全相同,并且精度也不相同)

  • ::GetTickCount64()

2020-07-18 17:36:41.512344

  • 事件日志

没有结果,因为事件日志条目目前在我的系统上不存在(最早的事件是从7月23日开始)

  • wmic OS Get LastBootUpTime on PID 4:

2020-07-18 17:36:48.0424863

与其他方法相差几秒钟,但我想不出它本身是错误的任何方式,因为如果系统进程尚未运行,则系统实际启动了吗?

答案 3 :(得分:0)

C ++ Boost used to use WMI LastBootUpTime but switched, in version 1.54, to checking the system event log, and apparently for a good reason:

  

ABI中断:已更改Windows中的启动时间戳功能,以将EventLog服务启动时间用作系统启动时间。 WMI先前使用的LastBootupTime由于时间同步和休眠而不稳定,在实践中无法使用。如果您确实需要获得Boost 1.54之前的行为,请从命令行或detail / workaround.hpp中定义BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME

在第2201行附近检查boost / interprocess / detail / win32_api.hpp,以函数inline bool get_last_bootup_time(std::string &stamp)的实现为例。 (如果您要匹配行号,我正在使用版本1.60。)

以防万一Boost死了,而我指的是Boost没有帮助(是的),您想要的功能主要是ReadEventLogA和要查找的事件ID(“事件日志已开始“根据Boost的评论)显然是6005

答案 4 :(得分:0)

这不是一个单独的答案,但是我实现了其他三个答案,并且有一些观察结果可能对那些尝试选择方法的人有所帮助。

使用GetTickCount64并从当前时间中减去

  • 最快的方法,以0.112毫秒的速度计时。
  • 在其参数的100 ns分辨率下不会产生唯一/一致的值,因为它取决于时钟滴答。返回的值都在彼此的1/64秒之内。
  • 需要Vista或更高版本。如果您的应用程序/库必须支持较早的Windows版本,则XP的32位计数器会在约49天后过期,因此不能用于此方法。

使用LastBootUpTime的{​​{1}}字段的WMI查询

  • 使用COM花费84毫秒,使用Win32_OperatingSystem命令行花费202毫秒。
  • wmic字符串的形式产生一致的值
  • WMI类需要Vista或更高版本。

阅读事件日志

  • 最慢的方法,耗时229毫秒
  • 以秒(Unix时间)为单位产生一致的值
  • 在Windows 2000或更高版本上工作。

这些方法还产生了不同的时间戳:

  • 正常运行时间: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时间中产生一致的时间戳,不受睡眠/休眠周期的影响,但也是最慢的。鉴于这不太可能循环运行,因此它可能是最佳选择。