How To: Reading the Windows NT event log

Posted: (EET/GMT+2)

 

How To: Reading the Windows NT event log

Level: Delphi, Win32 Intermediate


The Windows NT event log contains events recorded both by the system and user applications. You can see the events in the event log using the Event Viewer, by choosing Start/Programs/Administrative Tools/Event Viewer.

In this How To you will learn how to read the event log and scan for the system startup event (event ID 512). The sample application also creates an icon to the task tray. (For more information about this, see Creating simple task tray apps.) When you move the mouse pointer over the icon, you will see for how long your NT system has been up and running.

Reading the event log

To read the event log, three Win32 API functions are needed. The first function, OpenEventLog opens a handle to the event log. ReadEventLog is used to read the events, and finally the code closes the handle to the log by calling CloseEventLog.

The following code does just this:

    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;
    
Here, the event log is read "backwards" so that we first read the newest events. When we find the system startup event (ID 512), we must convert the event logging time (given in seconds since 1st January, 1970) into a TDateTime which Delphi uses. This is done by dividing by the constant 86400 (seconds in a day) and addind a "magic" constant. After the data is in a TDateTime, it is easy to calculate the "up time" with the code:
    RunTime := Now-SystemStartupTime+1;
Note that the two constants, EventLog_Backwards_Read and EventLog_Sequential_Read are not defined in Delphi's Windows.PAS. Instead, I've converted the constants from WINNT.H as follows:
    Const
      EventLog_Sequential_Read = $1;
      EventLog_Backwards_Read  = $8;
    
Also, the same applies to the TEventLogRecords. It is defined as follows:
    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;
    

Conclusion

Reading the event log is quite easy with some help of the C-language header files. Converting the data types and constants is not hard, but I hope Inprise will do better work next time.