Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically getting system boot up time in c++ (windows)

Tags:

c++

c

windows

So quite simply, the question is how to get the system boot up time in windows with c/c++.

Searching for this hasn't got me any answer, I have only found a really hacky approach which is reading a file timestamp ( needless to say, I abandoned reading that halfway ).

Another approach that I found was actually reading windows diagnostics logged events? Supposedly that has last boot up time.

Does anyone know how to do this (with hopefully not too many ugly hacks)?

like image 283
Eximius Avatar asked Jun 01 '12 16:06

Eximius


4 Answers

GetTickCount64 "retrieves the number of milliseconds that have elapsed since the system was started."

Once you know how long the system has been running, it is simply a matter of subtracting this duration from the current time to determine when it was booted. For example, using the C++11 chrono library (supported by Visual C++ 2012):

auto uptime = std::chrono::milliseconds(GetTickCount64());
auto boot_time = std::chrono::system_clock::now() - uptime;
like image 94
James McNellis Avatar answered Oct 23 '22 00:10

James McNellis


You can also use WMI to get the precise time of boot. WMI is not for the faint of heart, but it will get you what you are looking for.

The information in question is on the Win32_OperatingSystem object under the LastBootUpTime property. You can examine other properties using WMI Tools.

WMI Explorer showing property instance

Edit: You can also get this information from the command line if you prefer.

wmic OS Get LastBootUpTime

As an example in C# it would look like the following (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();
}
like image 8
linuxuser27 Avatar answered Oct 22 '22 23:10

linuxuser27


The "System Up Time" performance counter on the "System" object is another source. It's available programmatically using the PDH Helper methods. It is, however, not robust to sleep/hibernate cycles so is probably not much better than GetTickCount()/GetTickCount64().

Reading the counter returns a 64-bit FILETIME value, the number of 100-NS ticks since the Windows Epoch (1601-01-01 00:00:00 UTC). You can also see the value the counter returns by reading the WMI table exposing the raw values used to compute this. (Read programmatically using COM, or grab the command line from wmic:)

wmic path Win32_PerfRawData_PerfOS_System  get systemuptime

That query produces 132558992761256000 for me, corresponding to Saturday, January 23, 2021 6:14:36 PM UTC.

You can use the PerfFormattedData equivalent to get a floating point number of seconds, or read that from the command line in wmic or query the counter in PowerShell:

Get-Counter -Counter '\system\system up time'

This returns an uptime of 427.0152 seconds.

I also implemented each of the other 3 answers and have some observations that may help those trying to choose a method.

Using GetTickCount64 and subtracting from current time

  • The fastest method, clocking in at 0.112 ms.
  • Does not produce a unique/consistent value at the 100-ns resolution of its arguments, as it is dependent on clock ticks. Returned values are all within 1/64 of a second of each other.
  • Requires Vista or newer. XP's 32-bit counter rolls over at ~49 days and can't be used for this approach, if your application/library must support older Windows versions

Using WMI query of the LastBootUpTime field of Win32_OperatingSystem

  • Took 84 ms using COM, 202ms using wmic command line.
  • Produces a consistent value as a CIM_DATETIME string
  • WMI class requires Vista or newer.

Reading Event Log

  • The slowest method, taking 229 ms
  • Produces a consistent value in units of seconds (Unix time)
  • Works on Windows 2000 or newer.
  • As pointed out by Jonathan Gilbert in the comments, is not guaranteed to produce a result.

The methods also produced different timestamps:

  • UpTime: 1558758098843 = 2019-05-25 04:21:38 UTC (sometimes :37)
  • WMI: 20190524222528.665400-420 = 2019-05-25 05:25:28 UTC
  • Event Log: 1558693023 = 2019-05-24 10:17:03 UTC

Conclusion:

The Event Log method is compatible with older Windows versions, produces a consistent timestamp in unix time that's unaffected by sleep/hibernate cycles, but is also the slowest. Given that this is unlikely to be run in a loop it's this may be an acceptable performance impact. However, using this approach still requires handling the situation where the Event log reaches capacity and deletes older messages, potentially using one of the other options as a backup.

like image 4
Daniel Widdis Avatar answered Oct 22 '22 23:10

Daniel Widdis


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 breaking: Changed bootstamp function in Windows to use EventLog service start time as system bootup time. Previously used LastBootupTime from WMI was unstable with time synchronization and hibernation and unusable in practice. If you really need to obtain pre Boost 1.54 behaviour define BOOST_INTERPROCESS_BOOTSTAMP_IS_LASTBOOTUPTIME from command line or detail/workaround.hpp.

Check out boost/interprocess/detail/win32_api.hpp, around line 2201, the implementation of the function inline bool get_last_bootup_time(std::string &stamp) for an example. (I'm looking at version 1.60, if you want to match line numbers.)

Just in case Boost ever dies somehow and my pointing you to Boost doesn't help (yeah right), the function you'll want is mainly ReadEventLogA and the event ID to look for ("Event Log Started" according to Boost comments) is apparently 6005.

like image 3
Keith M Avatar answered Oct 23 '22 01:10

Keith M