2

How to get the date & time of the last boot / reboot / restart on Windows 2008/2003 machine?

I know from command prompt we can use "net statistics", but how to do it via Delphi?

Thanks.

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
Kawaii-Hachii
  • 1,017
  • 7
  • 22
  • 36

4 Answers4

7

You can use the LastBootUpTime property of the Win32_OperatingSystem WMI Class, which return the Date and time the operating system was last restarted (Note : the returned value of this property is in UTC format).

Check this sample app

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ActiveX,
  Variants,
  ComObj;


//Universal Time (UTC) format of YYYYMMDDHHMMSS.MMMMMM(+-)OOO.
//20091231000000.000000+000
function UtcToDateTime(const V : OleVariant): TDateTime;
var
  Dt : OleVariant;
begin
  Result:=0;
  if VarIsNull(V) then exit;
  Dt:=CreateOleObject('WbemScripting.SWbemDateTime');
  Dt.Value := V;
  Result:=Dt.GetVarDate;
end;

procedure  GetWin32_OperatingSystemInfo;
const
  WbemUser            ='';
  WbemPassword        ='';
  WbemComputer        ='localhost';
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT * FROM Win32_OperatingSystem','WQL',wbemFlagForwardOnly);
  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  if oEnum.Next(1, FWbemObject, iValue) = 0 then
  begin
    Writeln(Format('Last BootUp Time    %s',[FWbemObject.LastBootUpTime]));// In utc format
    Writeln(Format('Last BootUp Time    %s',[formatDateTime('dd-mm-yyyy hh:nn:ss',UtcToDateTime(FWbemObject.LastBootUpTime))]));// Datetime
  end;
end;


begin
 try
    CoInitialize(nil);
    try
      GetWin32_OperatingSystemInfo;
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • - the more i look at this obscure solution, the less i like it. Esp: "LastBootUpTime Data type: **datetime**" Stringly-typed scripting, right? – Premature Optimization Jun 29 '11 at 16:35
  • 3
    @Downvoter: I have to agree with RRUZ. WMI is pretty documented through MS Platform SDK. There's no obscurity here. – Fabricio Araujo Jun 29 '11 at 19:32
  • +1; I found `LastBootUpTime` through your [WMI Delphi Code Creator](http://theroadtodelphi.wordpress.com/wmi-delphi-code-creator/) few hours ago but I knew you'll want to post the answer :) One don't believe how many secrets WMI has when I was browsing through [the reference](http://msdn.microsoft.com/en-us/library/aa394572%28v=VS.85%29.aspx) some time ago. –  Jun 29 '11 at 19:56
  • @RRUZ: every variable is OleVariant, why? (hiding relevant `pclsObj->Get(L"LastBootUpTime", 0, &vtProp, 0, 0)` call, too) `SELECT *`, why? and as i said already - datetime converted to string and then back to number, why? – Premature Optimization Jun 29 '11 at 22:02
  • @Downvoter , I think you are a little confused, 1) the code uses Late-binding and the WMI scripting library, the code which you suggest use the COM API 2) The Olevariants can be variants too, and these variables are used to hold the interfaces and objects of the WMI.3) I use late binding to avoid to use the WbemScripting_TLB unit which is generated by delphi when you import the WMI scripting library. – RRUZ Jun 29 '11 at 22:11
  • 4) I choose The `select * Win32_OperatingSystem`, because if only select the property `LastBootUpTime` in some older versions of the WMI that syntax is not supported `Select LastBootUpTime from Win32_OperatingSystem` 5) The value returned by the WMI is in UTC format not the native delphi TDatetime, because that is necessary convert this value to TDatetime. 6) I wrote several tools and articles about the WMI which you can check on my blog http://theroadtodelphi.wordpress.com/category/wmi/. – RRUZ Jun 29 '11 at 22:18
  • @RRUZ, you successfully avoided type library dependency at the cost of reinvented datetime string parsing (is stored as number, but retrieval method locked you into OleStr) IMO this is an abuse of the scripting sugar, and not appropriate for high level language. Imagine how much efforts it will take to maintain such untyped scripting obscurity! – Premature Optimization Jun 29 '11 at 22:47
  • @Downvoter, maybe you can post an answer with less "obscurity". – RRUZ Jun 29 '11 at 22:59
  • @Misha, I have windows 7 64 bits too, and the code works ok, which error do you have? any exception? – RRUZ Jun 30 '11 at 00:53
  • No, just the wrong boot time. I am about to reboot my machine anyway so will test after a reboot. – Misha Jun 30 '11 at 01:19
  • 1
    @Misha, how you know which is the wrong time? try comparing the value returned against this `systeminfo | find "System Boot Time:"` (run from the CMD.exe) – RRUZ Jun 30 '11 at 01:33
  • @RRUZ, I had a look at the event log. WMI gave me a date of the 23rd, the performance counter gave me a date of the 25th, and from the logs I could see a reboot on the 25th. – Misha Jun 30 '11 at 02:26
  • @RRUZ, seems to work on other machines, and after a reboot. A one-off glitch perhaps? – Misha Jun 30 '11 at 02:40
  • 1
    @Misha read this article http://www.petri.co.il/check-uptime-in-server-2008.htm here you will find the differences between the methods to determine the windows uptime or the windows last boot time. – RRUZ Jun 30 '11 at 02:44
  • @RRUZ, i dont see the point, as **ain**'s answer (follow the links!) covered all of methods. I dont see WMI (even bare WMI, not to say about its WBEM bindings) as "appropriate" tool for such simple task, sorry. – Premature Optimization Jun 30 '11 at 13:15
  • I am gettting my system boot time as 11:36:53 am , however using systeminfo command , i get the system up time as 11:37:10 am , what is wrong here ? – CyprUS Apr 25 '12 at 09:39
3

Here is a complete command line application that does what you are talking about. I've modified this to avoid the GetTickCount overflow issues without relying on external function calls.

Example output:

Windows was last rebooted at: 06/29/2011 9:22:47 AM

Have fun!

program lastboottime;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows;

function UptimeInDays: double;
const
  c_SecondsInADay = 86400;
var
  cnt, freq: Int64;
begin
  QueryPerformanceCounter(cnt);
  QueryPerformanceFrequency(freq);
  Result := (cnt / freq) / c_SecondsInADay;
end;

function LastBootTime: TDateTime;
begin
  Result := Now() - UptimeInDays;
end;

begin
  try
    WriteLn('Windows was last rebooted at: ' + DateTimeToStr(LastBootTime));
    ReadLn;
  except on E: Exception do
    Writeln(E.ClassName, ': ', E.Message);
  end;
end.
JosephStyons
  • 57,317
  • 63
  • 160
  • 234
  • I guess it would be unfair of me *not* to give a downvote here as well... Sorry... :( – Andreas Rejbrand Jun 29 '11 at 14:02
  • This is essentially what I use and works on my Win7 64 laptop whereas the WMI version does not. For 1 week up-time my version is accurate to +/- around 10 seconds – Misha Jun 30 '11 at 00:51
  • The WMI version also appears to work, but for one blip, which since I have rebooted the offending machine, cannot be tested. – Misha Jun 30 '11 at 06:05
2

Here's a bit of code that uses GetTickCount64 if available and falls back to GetTickCount if unavailable to compute the date and time of system startup. This is not a perfect solution because GetTickCount64 is only supported on Vista+ : if you're on older Windows, the counter goes back to 0 every 49 days.

program Project29;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

type
  TGetTickCount64 = function : Int64; stdcall;

var
  H_K32: HMODULE;
  Tick64Proc: TGetTickCount64;

function BootTime: TDateTime;
var UpTime: Int64;
    Seconds, Minutes, Hours: Int64;
begin
  if H_K32 = 0 then
  begin
    H_K32 := LoadLibrary(kernel32);
    if H_K32 = 0 then
      RaiseLastOSError
    else
      begin
        Tick64Proc := GetProcAddress(H_K32, 'GetTickCount64');
     end;
  end;

  if Assigned(Tick64Proc) then
    UpTime := Tick64Proc
  else
    UpTime := GetTickCount;

  Result := Now - EncodeTime(0, 0, 0, 1) * UpTime;
end;

begin
  WriteLn(DateTimeToStr(BootTime));
  ReadLn;
end.
Cosmin Prund
  • 25,498
  • 2
  • 60
  • 104
  • I think this is the best I can get. Tested on Windows 2003 server, it uses GetTickCount. While Windows 2008 server uses GetTickCount64. It shame that there is no simple API to get this information though. – Kawaii-Hachii Jun 29 '11 at 14:36
  • @ewlung, there is, "System Up Time" performance counter, see **ain**'s answer. – Premature Optimization Jun 29 '11 at 16:04
1

The GetTickCount function (see MSDN) returns the number of milliseconds that have elapsed since the system was started, so divide it with 1000 to get seconds, with 60 000 to get minutes etc.

The topic I linked also contains this bit:

To obtain the time elapsed since the computer was started, retrieve the System Up Time counter in the performance data in the registry key HKEY_PERFORMANCE_DATA. The value returned is an 8-byte value. For more information, see Performance Counters.

ain
  • 22,394
  • 3
  • 54
  • 74
  • +1, you can substract the runtime from the current time and you'll get the boot time. – Cosmin Prund Jun 29 '11 at 13:36
  • 2
    -1 because this doesn't work. `GetTickCount` returns a 32-bit unsigned integer, the maximum value of which is `4294967295`. Intepreted as a number of milliseconds, this equals slightly less than 50 days. That is, when the computer has been running for 50 days, the tick count-approach will incorrectly say that the computer was just recently started. – Andreas Rejbrand Jun 29 '11 at 13:37
  • 1
    That's why you use the `GetTickCount64` on Vista+, but pretty close to the correct answer. – Cosmin Prund Jun 29 '11 at 13:42
  • 2
    @Andreas It will roll over after 49.7 days. There is also GetTickCount64 function which "should be enough for everybody", but it is supported from Vista on (BTW noticed now that this is what OP is using). I also quoted the performance counter info which doesn't suffer from the 32 bit limitation. – ain Jun 29 '11 at 13:47
  • 2
    What about when you suspend your machine? Doesn't that stop the tick counter? – David Heffernan Jun 29 '11 at 13:52
  • How do I use GetTickCount64? Does it work on Windows 2003? Well, the reason for this question, I want to prevent my application to start if the Windows machine "uptime" is less than 15 minutes. So, I want to solve this by getting the last boot time and compare it with current time. – Kawaii-Hachii Jun 29 '11 at 13:53
  • @David - good point; on MSDN says `since the system was started` but I would say it will. If not then it's a [duplicate](http://stackoverflow.com/q/1645896/532231). –  Jun 29 '11 at 13:58
  • @ewlung It's hard to imagine why you would want to stop your app from starting until uptime>15 minutes. That seems pretty user hostile to me. – David Heffernan Jun 29 '11 at 13:59
  • @ewlung, `GetTickCount` is good enough if all you need is "less then 15 minutes". Sure, `GetTickCount` would give a fake "window of oportunity" for every 15 minutes once every 49.7 days, but I doubt that's a problem! Just do `if GetTickCount < (15*60*100) then`! – Cosmin Prund Jun 29 '11 at 14:08
  • @David: This app (running on server B) will check database connection to other server (server A) every 5 minutes and report. Often, when both servers are rebooted, the server A can take more than 15 minutes. So, I want to give some kind of grace period of the app on server B, to wait 15 minutes. That's all. – Kawaii-Hachii Jun 29 '11 at 14:10
  • Why don't you just add a retry mechanism? Or delay the start of the app. Start it in a .cmd script with a `sleep` command before you start it. It sounds like you're dealing with the problem in the wrong place. – David Heffernan Jun 29 '11 at 14:16
  • @David: this app is part of a "monitoring tool". It will be called once every 5 minutes by a service (third party). I just want to avoid false alarm because the database server (Server A) take a long time to reboot. Otherwise, this app will report "cannot connect to Server A". – Kawaii-Hachii Jun 29 '11 at 14:34
  • Then delay the startup. By forcing 15 minute uptime you'll not be able to reboot server A and then restart the app. I still think you are going about this the wrong way. – David Heffernan Jun 29 '11 at 14:40