Calculating Windows NT uptime with Delphi

Posted: (EET/GMT+2)

 

I was today testing if I could use Delphi to read the Windows NT Event Log and find the system startup event. It turned out this can be done, and here below is code that shows how this can be done.

The reading of events is done with the ReadEventLog Win32 API, and the event with id number 512 is the one to look for: "Windows NT is starting up." The time is returned as a long number that requires some calculation to become a Delphi TDateTime object.

The following code allows you to calculate the uptime:

Uses Messages,SysUtils,Windows;

Const
  EventLog_Sequential_Read = $1;
  EventLog_Backwards_Read  = $8;

Type
  PEventLogRecord = ^TEventLogRecord;
  TEventLogRecord = Record
    Length              : Integer;
    Reserved            : Integer;
    RecordNumber        : Integer;
    TimeGenerated       : Integer;
    TimeWritten         : Integer;
    EventID             : Integer;
    EventType           : Word;
    NumStrings          : Word;
    EventCategory       : Word;
    ReservedFlags       : Word;
    ClosingRecordNumber : Integer;
    StringOffset        : Integer;
    UserSidLength       : Integer;
    UserSidOffset       : Integer;
    DataLength          : Integer;
    DataOffset          : Integer;
    { optional data follows }
  End;

Var
  SystemStartupTime : TDateTime;
  SystemName        : String;

Function GetComputerUpTime : String;
Var RunTime : TDateTime;
Begin
  RunTime := Now-SystemStartupTime+1;
  Result := SystemName+IntToStr(Trunc(RunTime))+' day(s), '+TimeToStr(RunTime);
End;

Procedure ReadComputerName;
Var I : Integer;
Begin
  I := 255;
  SetLength(SystemName,I);
  If GetComputerName(PChar(SystemName),I) Then Begin
    SetLength(SystemName,I);
    SystemName := SystemName+' has been running for ';
  End
  Else SystemName := '';
End;

Procedure ScanEventLogForStartupEvent;
Var
  Log                    : THandle;
  Buf                    : Array[0..5000-1] of Char;
  BytesRead,MinBytes,Ofs : Integer;

Begin
  Log := OpenEventLog(nil,'Security');
  While (ReadEventLog(Log,EventLog_Backwards_Read Or EventLog_Sequential_Read,0,
                      @Buf,SizeOf(Buf),BytesRead,MinBytes)) do Begin
    Ofs := 0;
    While (BytesRead > 0) do Begin
      With PEventLogRecord(@Buf[Ofs])^ do Begin
        If (EventID = 512) Then Begin { "Windows NT is starting up." }
          SystemStartupTime := (TimeGenerated / (60*60*24))+25569.08333333333;
          CloseEventLog(Log);
          Exit;
        End;
        Inc(Ofs,Length);
        Dec(BytesRead,Length);
      End;
    End;
  End;
  CloseEventLog(Log);
  MessageBox(0,'Cannot find system startup event from event log.','UpTime',mb_IconError+mb_OK);
  Halt(2);
End;

For making the code run faster, I'm scanning the event log backwards, from newest entry to oldest.

To use the code, you would simply call the functions in order:

ReadComputerName();
ScanEventLogForStartupEvent();
WriteLn(GetComputerUpTime());

If it works, it should display your computer's name and the uptime in days.

What is the longest uptime you have had? Mine is about a week.