4.1 Obtain Records Stored in an Event Log

In this example, a client application wants to obtain records stored in an event log. This involves the following steps:

  1. To establish a connection to the server, the client application calls ElfrOpenELW with the following values for the parameters.

                 NTSTATUS = {to be filled in by server}
                 ElfrOpenELW(
                   [in] EVENTLOG_HANDLE_W 
                     UNCServerName = "servername",
                   [in] PRPC_UNICODE_STRING ModuleName = {"Application"},
                   [in] PRPC_UNICODE_STRING RegModuleName = {""},
                   [in] unsigned long MajorVersion = 0x00000001,
                   [in] unsigned long MinorVersion = 0x00000001,
                   [out] IELF_HANDLE * LogHandle = 
                     {to be filled in by server}
                 );
    

    The server verifies that the client application has read access, and, if so, returns a handle (LogHandle) to the client application. The server maintains an association between the handle and a particular event log and keeps track of the position of the last read operation, if any.

    In applicable Windows Server releases, the application log is typically configured in the Windows registry as follows:

    Name

    Type

    Data

    (Default)

    REG_SZ

    (value not set)

    AutoBackupLogFiles

    REG_DWORD

    0x00000000 (0)

    DisplayNameFile

    REG_EXPAND_SZ

    %SystemRoot%\system32\wevtapi.dll

    DisplayNameID

    REG_DWORD

    0x00000100 (256)

    File

    REG_EXPAND_SZ

    %SystemRoot%\system32\winevt\Logs\Application.evtx

    MaxSize

    REG_DWORD

    0x00ed0000 (15532032)

    PrimaryModule

    REG_SZ

    Application

    RestrictGuestAccess

    REG_DWORD

    0x00000001 (1)

    Retention

    REG_DWORD

    0x00000000 (0)

    If the log is successfully opened, in a Windows-based implementation, an IELF_HANDLE is created in the server side as follows:

      
     struct _ELF_HANDLE {
         ULONG    Signature; = 0x654c6648
         ULONG    Flags; = 0
         unsigned __int64 LastRecordRead; = -1 (not start reading)
         ULONG    MajorVersion;  = 0x00000001
         ULONG    MinorVersion;  = 0x00000001
         void*    LogPublisher;  = (a memory address pointing to event source.)
         ULONG    NameLength;    = 0x0000000b
         [size_is(NameLength)] WCHAR Name[]; = {"Application"}
     } *IELF_HANDLE;
    

    The server maintains the content of this data structure and only passes the pointer to the client.

  2. The client application then reads the records using the ElfrReadELW (section 3.1.4.7) method. The client application specifies the context handle (LogHandle) obtained in the previous step. To retrieve records in sequential order, the client application calls ElfrReadELW with the following parameters.

               NTSTATUS = {to be filled in by server}
               ElfrReadELW(
               [in] IELF_HANDLE LogHandle =
               {handle obtained by the call to ElfrOpenELW},
               [in] unsigned long ReadFlags = 0x00000005,
               [in] unsigned long RecordOffset = 0x00000000,
               [in, range(0, 0x7FFFF)]
               unsigned long NumberOfBytesToRead = 0x3ffff,
               [out, size_is(NumberOfBytesToRead)]
               unsigned char * Buffer = {to be filled in by server},
               [out] unsigned long * NumberOfBytesRead =
               {to be filled in by server},
               [out] unsigned long * MinNumberOfBytesNeeded =
               {to be filled in by server}
               );
      
    

    The server then returns one or more records. The number of records returned is limited by what fits in the buffer and by what is actually available in the log, whatever is less.

    If the buffer provided by the client can fit 5 records, and the client starts to read the log from the beginning, the LastRecordRead field value in IELF_HANDLE (depicted in step 1) will become 5 after this function returns.

    If the RecordOffset is a non-zero value, for instance, it is set as 0x00000500 and the sizes of the first three records in the event log file are 0x200, 0x250, and 0x140. The server gets the size value from the length field of the event log record that is specified in section 2.2.3. Since the offset value is larger than the total length of the first two records, but less than the total length of the first three, the server will round the value to the length of the first two records and start to read the third record.

  3. The client application can continue invoking this method to obtain additional records. When the client application is finished reading records, it releases the log handle by calling ElfrCloseEL as follows.

               NTSTATUS = {to be filled in by server} ElfrCloseEL(
               [in, out] IELF_HANDLE * LogHandle
               = {handle obtained from ElfrOpenELW}
               );
      
    

     The server removes its state for the handle and returns success.