很简单,问题是如何使用 c/c++ 在 windows 中获得系统启动时间。
搜索这个并没有给我任何答案,我只找到了一种非常hacky的方法,它正在读取文件时间戳(不用说,我中途放弃了阅读)。
我发现的另一种方法实际上是读取 Windows 诊断记录的事件?据说这是最后一次启动时间。
有谁知道如何做到这一点(希望没有太多丑陋的黑客)?
GetTickCount64
“检索自系统启动以来经过的毫秒数。”
一旦您知道系统运行了多长时间,只需从当前时间中减去此持续时间即可确定系统何时启动。例如,使用 C++11 chrono 库(Visual C++ 2012 支持):
auto uptime = std::chrono::milliseconds(GetTickCount64());
auto boot_time = std::chrono::system_clock::now() - uptime;
您还可以使用WMI获取准确的启动时间。WMI 不适合胆小的人,但它会为您提供所需的东西。
有问题的信息是关于该Win32_OperatingSystem
属性下的对象的LastBootUpTime
。您可以使用WMI Tools检查其他属性。
编辑: 如果您愿意,也可以从命令行获取此信息。
wmic OS Get LastBootUpTime
作为 C# 中的示例,它看起来如下所示(使用 C++ 相当冗长):
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();
}
“系统”对象上的“系统启动时间”性能计数器是另一个来源。它可以使用PDH Helper 方法以编程方式使用。但是,它对睡眠/休眠周期并不健壮,因此可能并不比GetTickCount()
/好多少GetTickCount64()
。
读取计数器返回一个 64 位FILETIME
值,即自 Windows Epoch (1601-01-01 00:00:00 UTC) 以来的 100-NS 滴答数。您还可以通过读取 WMI 表来查看计数器返回的值,该表公开了用于计算此值的原始值。(使用 COM 以编程方式读取,或从 wmic 获取命令行:)
wmic path Win32_PerfRawData_PerfOS_System get systemuptime
该查询为我生成 132558992761256000,对应于 UTC 时间 2021 年 1 月 23 日星期六 6:14:36 PM。
您可以使用PerfFormattedData
等效项来获取浮点秒数,或者从命令行中读取该秒数或wmic
在 PowerShell 中查询计数器:
Get-Counter -Counter '\system\system up time'
这将返回 427.0152 秒的正常运行时间。
我还实现了其他 3 个答案中的每一个,并有一些观察结果可以帮助那些试图选择一种方法的人。
当前时间的使用GetTickCount64
和减去
使用 WMI 查询LastBootUpTime
字段Win32_OperatingSystem
wmic
命令行需要 202 毫秒。CIM_DATETIME
字符串读取事件日志
这些方法还产生了不同的时间戳:
结论:
事件日志方法与旧的 Windows 版本兼容,在 unix 时间产生一致的时间戳,不受睡眠/休眠周期的影响,但也是最慢的。鉴于这不太可能在循环中运行,这可能是可接受的性能影响。但是,使用这种方法仍然需要处理事件日志达到容量并删除旧消息的情况,可能使用其他选项之一作为备份。
C++ Boost曾经使用 WMI LastBootUpTime
,但在 1.54 版中切换到检查系统事件日志,显然是有充分理由的:
ABI 中断:更改了 Windows 中的 bootstamp 功能,以使用 EventLog 服务启动时间作为系统启动时间。以前
LastBootupTime
从 WMI 使用的时间同步和休眠不稳定,在实践中无法使用。如果您确实需要BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME
从命令行或 detail/workaround.hpp 获取 Boost 1.54 之前的行为定义。
查看 boost/interprocess/detail/win32_api.hpp,在第 2201 行附近,该函数的inline bool get_last_bootup_time(std::string &stamp)
实现作为示例。(如果您想匹配行号,我正在查看版本 1.60。)
以防 Boost 以某种方式死去,而我将您指向 Boost 并没有帮助(是的,对),您需要的功能主要是ReadEventLogA
要查找的事件 ID(根据 Boost 评论“事件日志已启动”)显然6005
.
我没有玩过这么多,但我个人认为最好的方法可能是查询“系统”进程的开始时间。在 Windows 上,内核在启动时为自己的目的分配一个进程(令人惊讶的是,快速的谷歌搜索并不能轻易地发现它的实际目的是什么,尽管我确信信息就在那里)。此进程在任务管理器中简称为“系统”,并且在当前 Windows 版本上始终具有 PID 4(显然 NT 4 和 Windows 2000 可能已使用 PID 8)。只要系统正在运行,这个进程就永远不会退出,并且在我的测试中,就其元数据而言,它的行为就像一个成熟的进程。从我的测试来看,即使是非提升用户也可以打开 PID 4 的句柄,请求PROCESS_QUERY_LIMITED_INFORMATION
,GetProcessTimes
,它将lpCreationTime
使用进程启动时间的 UTC 时间戳填充 。据我所知,在系统进程运行之前,Windows 没有任何有意义的运行方式,所以这个时间戳几乎就是 Windows 启动的时间。
#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);
}
}
我当前启动的比较:
system_clock::now() - milliseconds(GetTickCount64())
: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
453:17.(结果因调用而异,因为
system_clock::now()
并且::GetTickCount64()
不会在完全相同的时间运行并且没有相同的精度)
wmic OS Get LastBootUpTime
2020-07-18 17:36:41.512344
没有结果,因为此时我的系统上不存在事件日志条目(最早的事件是从 7 月 23 日开始)
GetProcessTimes
在 PID 4 上:2020-07-18 17:36:48.0424863
这与其他方法有几秒钟的不同,但我想不出它本身有什么问题,因为如果系统进程还没有运行,系统是否真的启动了?