|
|
|
Click here to copy the following block | Private Declare Function ReadEventLog Lib "advapi32.dll" Alias "ReadEventLogA" ( _ ByVal hEventLog As Long, _ ByVal dwReadFlags As Long, _ ByVal dwRecordOffset As Long, _ lpBuffer As Any, _ ByVal nNumberOfBytesToRead As Long, _ pnBytesRead As Long, _ pnMinNumberOfBytesNeeded As Long) As Long |
Parameters
- hEventLog : [in] Handle to the event log to be read. This handle is returned by the OpenEventLog function.
- dwReadFlags : [in] Options for how the read operation is to proceed. This parameter must include one of the following values.
If the buffer is large enough, more than one record can be read at the specified seek position; you must specify one of the following flags to indicate the direction for successive read operations.
- dwRecordOffset : [in] Log-entry record number at which the read operation should start. This parameter is ignored unless dwReadFlags includes the EVENTLOG_SEEK_READ flag.
- lpBuffer : [out] Pointer to a buffer for the data read from the event log. This parameter cannot be NULL, even if the nNumberOfBytesToRead parameter is zero. The buffer will be filled with an EVENTLOGRECORD structure.
- nNumberOfBytesToRead : [in] Size of the buffer, in bytes. This function will read as many whole log entries as will fit in the buffer; the function will not return partial entries, even if there is room in the buffer.
- pnBytesRead : [out] Pointer to a variable that receives the number of bytes read by the function.
- pnMinNumberOfBytesNeeded : [out] Pointer to a variable that receives the number of bytes required for the next log entry. This count is valid only if ReadEventLog returns zero and GetLastError returns ERROR_INSUFFICIENT_BUFFER.
Return Values
If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error information, call GetLastError.
Here is the few steps to read from the NT EventLog.
Step 1: First of all you need to decide mode of navigation. In our case ReadFlags = EVENTLOG_FORWARDS_READ Or EVENTLOG_SEEK_READ. Which is exact seek by Event Record Number which is similar to Pramary Key in RDBMS. ReadEntry function in CEventLog class takes 2 parameters. First parameter is recordNumber which is absolute position of a single event record. Understand that Event Record Number can be different than Absolute position of that record in EventLog
Step 2: To read full event entry you must provide sufficient Buffer to hold event entry. There is no way to find exact size of event record before you execute ReadEventLog API. You can use the trick which I have used here. Note: EventRecord is a variable type of EVENTLOGRECORD structure |
When you call ReadEventLog first time then it will fail if you dont have enough buffer. You can check for ret = 0 And Err.LastDllError <> ERROR_INSUFFICIENT_BUFFER condition. After first call it will set MinBytesNeeded to bytes required to hold the record.
Step 3: Once you know the actual size needed to hold the buffer you can resize your buffer array and call ReadEventLog again as shown below. |
Click here to copy the following block | MinBytesNeeded = ((MinBytesNeeded + 3) \ 4) * 4 ReDim RecordBuffer(MinBytesNeeded - 1) As Byte
ret = ReadEventLog(m_hEventLog, _ ReadFlags, _ Offset, _ RecordBuffer(0), _ MinBytesNeeded, _ NumBytesRead, _ MinBytesNeeded) |
After successful call to ReadEventLog you can fill EventRecord variable which is |
Step 4: Now you have event data in RecordBuffer and EventRecord. You can get few event properties directly from EventRecord but for some variable length properties i.e Message, Category Name, User Name ... you have to perform extra work. Let me explain you how event record is stored in Event RecordBuffer. First EVENTLOGRECORD is stored in the RecordBuffer then all variable length data is stored. |
Click here to copy the following block |
Private Type EVENTLOGRECORD length As Long Reserved As Long RecordNumber As Long TimeGenerated As Long TimeWritten As Long EventId As Long EventType As Integer NumStrings As Integer EventCategory As Integer ReservedFlags As Integer ClosingRecordNumber As Long StringOffset As Long UserSidLength As Long UserSidOffset As Long DataLength As Long DataOffset As Long End Type |
Here is some guidelines about how to read some important properties from eventlog. Event Createation/Written Time : EventLog stores time in UTC format so you have to convert TimeGenerated/TimeWritten both to Local date/time using the following code. |
Click here to copy the following block | m_EventTimeWritten = UTCtoLocal(DateAdd("s", EventRecord.TimeWritten, #1/1/1970#)) m_EventTimeCreated = UTCtoLocal(DateAdd("s", EventRecord.TimeGenerated, #1/1/1970#)) |
Event Source Name : Source name is stored after all Fixed length data in other term after EVENTLOGRECORD. Use the following code to grab Source name |
Event Machine Name : Machine name is stored after source name so just add length of source name to the Source String pointer to get Machine name. Use the following code to grab Source name |
Event User Name : Event Log stores User SID instead of name. So your first task is to get SID from variable length data and then you can use LookupAccountSid function to get username from SID. You can find exact location in of SID in RecordBuffer using UserSidOffset. I have wrapped LookupAccountSid API in a easy to use function GetUserNameFromSID. Following line will show you how to get User name. |
Event Category Name : Event Log only stores CategoryId. For Category name you have to do lookup in Resource File for Category. You can use LoadLibraryEx, FormatMessage and FreeLibrary apis to get Categoryname from CategoryId.
Event Message : To get full description of event you have to perform several tasks. First of all you have to find EventId and all Insert strings for that event. Insert strings are stored as Null seperated strings. You can split all strings and build string array. I have created a function called GetFormattedString which takes actual Message stored in Message File and string Array of insert strings. GetFormattedString function will mearge insert strings into actual message. For example Actaul Message stored in message file : "Drive %1 has only %2 MB left" will be replace by something like "Drive C has only 20 MB left". MessageString will do everything for you. - Find insert string (null seperated placeholder strings) from RecordBuffer - Split into string array. - Find actual message associated with an EventId - Mearge Insert Strings with Actual Message String
Here is the example to get message of an event. |
Here is the full code for all steps described above |
Click here to copy the following block | Private Sub ClearVariables() m_EventCategoryString = "" m_EventComputerName = "" m_EventDescription = "" m_EventSourceName = "" m_EventUserName = "" End Sub
Public Function GetFirstEventLogRecord() As Long Dim FirstRecord As Long Call GetOldestEventLogRecord(m_hEventLog, FirstRecord) GetFirstEventLogRecord = FirstRecord End Function
Public Function ReadEntry(Optional RecordNumber As Long = 0, Optional ReadMsgDesc As Boolean = False) As Boolean Dim Offset As Long Dim RecordBuffer() As Byte Dim EventLogHwnd As Long Dim ReadFlags As Long Dim NumBytesToRead As Long Dim NumBytesRead As Long Dim NumRecords As Long Dim MinBytesNeeded As Long Dim ret As Long Dim FirstRecord As Long ReadFlags = EVENTLOG_FORWARDS_READ Or EVENTLOG_SEEK_READ FirstRecord = GetFirstEventLogRecord Offset = RecordNumber ClearVariables ret = ReadEventLog(m_hEventLog, _ ReadFlags, _ Offset, _ EventRecord, _ NumBytesToRead, _ NumBytesRead, _ MinBytesNeeded)
If ret = 0 And Err.LastDllError <> ERROR_INSUFFICIENT_BUFFER Then Exit Function ElseIf ret = 0 And Err.LastDllError = ERROR_INSUFFICIENT_BUFFER Then
MinBytesNeeded = ((MinBytesNeeded + 3) \ 4) * 4 ReDim RecordBuffer(MinBytesNeeded - 1) As Byte
ret = ReadEventLog(m_hEventLog, _ ReadFlags, _ Offset, _ RecordBuffer(0), _ MinBytesNeeded, _ NumBytesRead, _ MinBytesNeeded) If ret = 0 Then Exit Function End If
MoveMem EventRecord, RecordBuffer(0), Len(EventRecord) m_EventTimeWritten = UTCtoLocal(DateAdd("s", EventRecord.TimeWritten, #1/1/1970#)) m_EventTimeCreated = UTCtoLocal(DateAdd("s", EventRecord.TimeGenerated, #1/1/1970#)) m_EventSourceName = GetStrFromPtrA(VarPtr(RecordBuffer(Len(EventRecord)))) m_EventComputerName = GetStrFromPtrA(VarPtr(RecordBuffer(Len(EventRecord))) + Len(m_EventSourceName) + 1) If EventRecord.UserSidLength > 0 Then m_EventUserName = GetUserNameFromSID(VarPtr(RecordBuffer(EventRecord.UserSidOffset))) End If m_EventCategoryString = CategoryString(m_EventSourceName, EventRecord.EventCategory) If ReadMsgDesc = True Then m_EventDescription = MessageString(m_EventSourceName, EventRecord.EventId, RecordBuffer) End If End Function
Private Function GetUTCBias() As Single
Dim dl As Long, dt As Integer Dim myTZ As TIME_ZONE_INFORMATION
dl = GetTimeZoneInformation(myTZ) If dl = 2 Then dt = 1 Else dt = 0 GetUTCBias = (myTZ.Bias + (myTZ.DaylightBias) * dt) / 60 End Function
Private Function UTCtoLocal(ByVal UTCTime As Date) As Date Dim Bias As Single Bias = GetUTCBias If Bias \ 1 = Bias Then UTCtoLocal = DateAdd("h", -1 * Bias, UTCTime) Else UTCtoLocal = DateAdd("n", -1 * (IIf(Bias < 0, -30, 30)), _ DateAdd("h", -1 * (Bias \ 1), UTCTime)) End If End Function
Private Function CategoryString(SourceName As String, CategoryId As Integer) As String On Error Resume Next Dim strLib As String, strLibExpandedPath As String Dim lpEventStr As Long, ret As Long, hModule As Long, nChars As Long, Flags As Long
If CategoryId <= 0 Then CategoryString = "<none>": Exit Function
strLib = GetLib(SourceName, CategoryFileLib)
If strLib = "" Then AddLib SourceName strLib = GetLib(SourceName, CategoryFileLib) If strLib = "" Then CategoryString = "<none>": Exit Function End If
CategoryString = MessageStringFromLib(strLib, CLng(CategoryId)) End Function
Private Function GetLib(EventSource As String, LType As LibType) As String On Error GoTo errHandler If LType = CategoryFileLib Then GetLib = colCategoryFileLib(EventSource) ElseIf LType = MessgeFileLib Then GetLib = colMessageFileLib(EventSource) ElseIf LType = ParameterFileLib Then GetLib = colParaMessageFileLib(EventSource) End If errHandler: End Function
Private Sub AddLib(EventSource As String) On Error GoTo errHandler Dim CatLibPath As String, MsgLibPath As String, ParaLibPath As String CatLibPath = ReadFromRegistry(HKEY_LOCAL_MACHINE, BASE_KEY & "\" & _ m_EventLogName & "\" & EventSource, "CategoryMessageFile") MsgLibPath = ReadFromRegistry(HKEY_LOCAL_MACHINE, BASE_KEY & "\" & _ m_EventLogName & "\" & EventSource, "EventMessageFile") ParaLibPath = ReadFromRegistry(HKEY_LOCAL_MACHINE, BASE_KEY & "\" & _ m_EventLogName & "\" & EventSource, "ParameterMessageFile")
If CatLibPath <> "" Then colCategoryFileLib.Add GetExpandedPath(CatLibPath), EventSource If MsgLibPath <> "" Then colMessageFileLib.Add GetExpandedPath(MsgLibPath), EventSource If ParaLibPath <> "" Then colParaMessageFileLib.Add GetExpandedPath(ParaLibPath), EventSource errHandler: End Sub
Private Function GetUserNameFromSID(ByVal lpSid As Long) As String Dim Success As Boolean Dim peUse As Long, UserID As String, DomainName As String
UserID = String(64, 0) DomainName = String(64, 0)
Success = LookupAccountSid(vbNullString, _ ByVal lpSid, _ UserID, _ Len(UserID), _ DomainName, _ Len(DomainName), _ peUse)
If Success Then GetUserNameFromSID = TrimNulls(UserID) End If End Function
Private Function TrimNulls(InString As String) As String Dim Pos As Long Pos = InStr(InString, Chr$(0)) If Pos > 0 Then TrimNulls = Left$(InString, Pos - 1) Else TrimNulls = InString End If End Function
Private Function MessageString(SourceName As String, Event_Id As Long, _ RecordBuffer() As Byte) As String
On Error Resume Next Dim lpEventStr As Long, ret As Long, hModule As Long, nChars As Long, Flags As Long Dim InsertionStrings As String, Msg As String, strLib As String, lngStringLen As Long
If Event_Id = 0 Then MessageString = "<none>": Exit Function
strLib = GetLib(SourceName, MessgeFileLib)
If strLib = "" Then AddLib SourceName strLib = GetLib(SourceName, MessgeFileLib) If strLib = "" Then MessageString = "<none>": Exit Function End If
lngStringLen = (EventRecord.DataOffset - EventRecord.StringOffset) Dim strStrings() As String
InsertionStrings = GetStrFromPtrA(VarPtr(RecordBuffer(EventRecord.StringOffset)), lngStringLen)
strStrings = Split(InsertionStrings, Chr(0)) If EventRecord.NumStrings > 0 Then ReDim Preserve strStrings(EventRecord.NumStrings - 1)
Msg = MessageStringFromLib(strLib, EventRecord.EventId)
If EventRecord.NumStrings > 0 Then If Msg <> "" Then MessageString = GetFormattedString(Msg, strStrings) Else MessageString = strStrings(0) End If Else MessageString = Msg End If End Function
Private Function GetFormattedString(strSource As String, Inserts() As String) As String Dim sSource As String, sBuf As String, sAnsiInserts() As String Dim i As Integer, iLB As Integer, iUB As Integer Dim lpInserts() As Long, lBufSize As Long, lRet As Long, lpInsertionStrings As Long
sSource = strSource
iLB = LBound(Inserts): iUB = UBound(Inserts) ReDim lpInserts(iLB To iUB) ReDim sAnsiInserts(iLB To iUB)
For i = iLB To iUB sBuf = Inserts(i) If InStr(1, sBuf, "%%") > 0 Then Inserts(i) = MessageStringFromLib(GetLib(Me.EventSourceName, ParameterFileLib) _ , Val(Right(sBuf, Len(sBuf) - 2)))
lBufSize = Len(Inserts(i)) * 2 End If
sAnsiInserts(i) = StrConv(Inserts(i), vbFromUnicode) lpInserts(i) = StrPtr(sAnsiInserts(i)) Next
Dim lpsBuf As Long, lpSource As Long lpInsertionStrings = VarPtr(lpInserts(0)) lBufSize = lBufSize + Len(strSource) + 2 + (EventRecord.DataOffset - EventRecord.StringOffset) * 2
sSource = StrConv(sSource, vbFromUnicode) lpSource = StrPtr(sSource)
lRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER Or _ FORMAT_MESSAGE_FROM_STRING Or _ FORMAT_MESSAGE_ARGUMENT_ARRAY, _ ByVal lpSource, 0&, 0&, lpsBuf, lBufSize, ByVal lpInsertionStrings)
sBuf = GetStrFromPtrA(lpsBuf, lBufSize) GetFormattedString = Left$(sBuf, lRet) End Function
Private Function MessageStringFromLib(strLib As String, MessageId As Long) As String Dim hModule As Long, lpEventStr As Long, nChars As Long, Flags As Long
hModule = LoadLibraryEx(strLib, 0&, LOAD_LIBRARY_AS_DATAFILE)
If hModule > 0 Then Flags = FORMAT_MESSAGE_FROM_HMODULE Or _ FORMAT_MESSAGE_IGNORE_INSERTS Or _ FORMAT_MESSAGE_ALLOCATE_BUFFER
nChars = FormatMessage(Flags, ByVal hModule, MessageId, _ 0&, ByVal VarPtr(lpEventStr), 0&, ByVal 0&)
MessageStringFromLib = GetStrFromPtrA(lpEventStr) If Right$(MessageStringFromLib, 2) = vbCrLf Then MessageStringFromLib = Left$(MessageStringFromLib, Len(MessageStringFromLib) - 2) End If Else MessageStringFromLib = "" End If
FreeLibrary hModule End Function
Private Function GetExpandedPath(ByVal strPath As String) As String Dim strExpandedPath As String strPath = UCase$(strPath) If InStr(strPath, "%") > 0 Then strExpandedPath = Space$(256) ret = ExpandEnvironmentStrings(strPath, strExpandedPath, Len(strExpandedPath)) If ret > 0 Then GetExpandedPath = Mid$(strExpandedPath, 1, ret - 1) End If Else GetExpandedPath = strPath End If End Function
Private Function GetStrFromPtrA(ByVal lngPtrString As Long, Optional lngLen As Long = 0) As String Dim lngNumChars As Long If lngLen = 0 Then lngNumChars = lstrlenA(ByVal lngPtrString) Else lngNumChars = lngLen End If
GetStrFromPtrA = Space$(lngNumChars) MultiByteToWideChar 0&, _ 0, _ ByVal lngPtrString, _ lngNumChars, _ ByVal StrPtr(GetStrFromPtrA), _ lngNumChars End Function
Private Function LoWord(ByVal lngNumber As Long) As Long LoWord = lngNumber And &HFFFF& End Function
Public Property Get EventTimeCreated() As Date EventTimeCreated = m_EventTimeCreated End Property Public Property Get EventSourceName() As String EventSourceName = m_EventSourceName End Property Public Property Get EventUserName() As String EventUserName = m_EventUserName End Property Public Property Get EventComputerName() As String EventComputerName = m_EventComputerName End Property Public Property Get EventType() As String Select Case EventRecord.EventType Case enumLogEntryTypes.EVENTLOG_INFORMATION_TYPE EventType = "Info" Case enumLogEntryTypes.EVENTLOG_ERROR_TYPE EventType = "Error" Case enumLogEntryTypes.EVENTLOG_SUCCESS EventType = "Success" Case enumLogEntryTypes.EVENTLOG_WARNING_TYPE EventType = "Warning" Case enumLogEntryTypes.EVENTLOG_AUDIT_FAILURE EventType = "Audit Failure" Case enumLogEntryTypes.EVENTLOG_AUDIT_SUCCESS EventType = "Audit Success" End Select End Property Public Property Get EventDescription() As String EventDescription = m_EventDescription End Property Public Property Get EventCategoryString() As String EventCategoryString = m_EventCategoryString End Property Public Property Get EventRecordNum() As Long EventRecordNum = EventRecord.RecordNumber End Property Public Property Get EventId() As Long EventId = LoWord(EventRecord.EventId) End Property |
|
|
|
Submitted By :
Nayan Patel
(Member Since : 5/26/2004 12:23:06 PM)
|
|
|
Job Description :
He is the moderator of this site and currently working as an independent consultant. He works with VB.net/ASP.net, SQL Server and other MS technologies. He is MCSD.net, MCDBA and MCSE. In his free time he likes to watch funny movies and doing oil painting. |
View all (893) submissions by this author
(Birth Date : 7/14/1981 ) |
|
|