Jump to content

Recommended Posts

Posted

Using EventLog__Read() to search recent logs for Application Error events. After finding one and parsing the info, the [13] value in the returned array does not properly contain the application name or version; instead it reads %1 and %2. Here's a side-by-side of the actual event log and a message box of $array[13] as returned by EventLog__Read()

image.png.b5930c71b62112ef4b7f6bc4dd8e444a.png

 

Script:

#include <EventLog.au3>
#include <Array.au3>


$hEventLog = _EventLog__Open("", "Application")
While 1
    $m = _EventLog__Read($hEventLog, True, False)
    If IsArray($m) Then
        If $m[10] = "Application error" Then
            MsgBox(0,"",$m[13])
        EndIf
    EndIf
WEnd
_EventLog__Close($hEventLog)

 

Posted (edited)

..solution is to change/fix __EventLog_DecodeDesc(), but here is a hack for now: 

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Version=Beta
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <EventLog.au3>
#include <FontConstants.au3>
#include <GUIConstantsEx.au3>

Global $g_idMemo = 0, $g_StrMemo = ""

Example()

Func Example()
    Local $hEventLog, $aEvent

    ; Create GUI
    GUICreate("EventLog  [ x64 = " & Not Not @AutoItX64 & " ][ AutoIt = v" & @AutoItVersion & " ]", 800, 400)
    $g_idMemo = GUICtrlCreateEdit("", 0, 0, 800, 400, 0)
    GUICtrlSetFont($g_idMemo, 9, $FW_NORMAL, $GUI_FONTNORMAL, "Courier New")
    GUISetState(@SW_SHOW)

    ; Read most current event record
    $hEventLog = _EventLog__Open("", "Application")
    Do
        $aEvent = _EventLog__Read2($hEventLog, True, False) ; read last event
        GUICtrlSetData($g_idMemo, @CRLF & @CRLF & @CRLF & @TAB & $aEvent[1] & @CRLF & @TAB & $aEvent[2] & " " & $aEvent[3])
        If GUIGetMsg() = $GUI_EVENT_CLOSE Or $aEvent[0] = False Then
            _EventLog__Close($hEventLog)
            GUIDelete()
            Exit 3
        EndIf
    Until StringInStr($aEvent[10], "Application error")

    MemoWrite("Result ............: " & $aEvent[0])
    MemoWrite("Record number .....: " & $aEvent[1])
    MemoWrite("Submitted .........: " & $aEvent[2] & " " & $aEvent[3])
    MemoWrite("Generated .........: " & $aEvent[4] & " " & $aEvent[5])
    MemoWrite("Event ID ..........: " & $aEvent[6])
    MemoWrite("Type ..............: " & $aEvent[8])
    MemoWrite("Category ..........: " & $aEvent[9])
    MemoWrite("Source ............: " & $aEvent[10])
    MemoWrite("Computer ..........: " & $aEvent[11])
    MemoWrite("Username ..........: " & $aEvent[12])
    MemoWrite("Description .......: " & $aEvent[13])
    MemoWrite()

    _EventLog__Close($hEventLog)

    ; Loop until the user exits.
    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE
    GUIDelete()
EndFunc   ;==>Example

; Write a line to the memo control
Func MemoWrite($sMessage = Default)
    If $sMessage = Default Then Return GUICtrlSetData($g_idMemo, $g_StrMemo)
    $g_StrMemo &= $sMessage & @CRLF
EndFunc   ;==>MemoWrite

Func _EventLog__Read2($hEventLog, $fRead = True, $fForward = True, $iOffset = 0)
    ; https://www.autoitscript.com/forum/topic/203482-eventlog__read-returning-1-2-instead-of-application-name/?do=findComment&comment=1461138
    Local $iReadFlags, $aEvent[15]
    $aEvent[0] = False ; in cas of error

    If $fRead Then
        $iReadFlags = $EVENTLOG_SEQUENTIAL_READ
    Else
        $iReadFlags = $EVENTLOG_SEEK_READ
    EndIf
    If $fForward Then
        $iReadFlags = BitOR($iReadFlags, $EVENTLOG_FORWARDS_READ)
    Else
        $iReadFlags = BitOR($iReadFlags, $EVENTLOG_BACKWARDS_READ)
    EndIf

    ; First call gets the size for the buffer.  A fake buffer is passed because
    ; the function demands the buffer be non-NULL even when requesting the size.
    Local $tBuffer = DllStructCreate("wchar[1]")
    Local $aResult = DllCall("advapi32.dll", "bool", "ReadEventLogW", "handle", $hEventLog, "dword", $iReadFlags, "dword", $iOffset, _
            "struct*", $tBuffer, "dword", 0, "dword*", 0, "dword*", 0)
    If @error Then Return SetError(@error, @extended, $aEvent)

    ; Allocate the buffer and repeat the call obtaining the information.
    Local $iBytesMin = $aResult[7]
    $tBuffer = DllStructCreate("wchar[" & $iBytesMin + 1 & "]")
    $aResult = DllCall("advapi32.dll", "bool", "ReadEventLogW", "handle", $hEventLog, "dword", $iReadFlags, "dword", $iOffset, _
            "struct*", $tBuffer, "dword", $iBytesMin, "dword*", 0, "dword*", 0)
    If @error Or Not $aResult[0] Then Return SetError(@error, @extended, $aEvent)

    Local $tEventLog = DllStructCreate($tagEVENTLOGRECORD, DllStructGetPtr($tBuffer))
    $aEvent[0] = True
    $aEvent[1] = DllStructGetData($tEventLog, "RecordNumber")
    $aEvent[2] = __EventLog_DecodeDate(DllStructGetData($tEventLog, "TimeGenerated"))
    $aEvent[3] = __EventLog_DecodeTime(DllStructGetData($tEventLog, "TimeGenerated"))
    $aEvent[4] = __EventLog_DecodeDate(DllStructGetData($tEventLog, "TimeWritten"))
    $aEvent[5] = __EventLog_DecodeTime(DllStructGetData($tEventLog, "TimeWritten"))
    $aEvent[6] = __EventLog_DecodeEventID($tEventLog)
    $aEvent[7] = DllStructGetData($tEventLog, "EventType")
    $aEvent[8] = __EventLog_DecodeTypeStr(DllStructGetData($tEventLog, "EventType"))
    $aEvent[9] = __EventLog_DecodeCategory($tEventLog)
    $aEvent[10] = __EventLog_DecodeSource($tEventLog)
    $aEvent[11] = __EventLog_DecodeComputer($tEventLog)
    $aEvent[12] = __EventLog_DecodeUserName($tEventLog)
    $aEvent[13] = __EventLog_DecodeDesc_3($tEventLog) ; <--- the offending function
    $aEvent[14] = __EventLog_DecodeData($tEventLog)
    Return $aEvent
EndFunc   ;==>_EventLog__Read2

Func __EventLog_DecodeDesc_3($tEventLog)
    ; https://www.autoitscript.com/forum/topic/203482-eventlog__read-returning-1-2-instead-of-application-name/?do=findComment&comment=1461138
    Local $aStrings = __EventLog_DecodeStrings($tEventLog)
    Local $sSource = __EventLog_DecodeSource($tEventLog)
    Local $iEventID = DllStructGetData($tEventLog, "EventID")
    Local $sKey = "HKLM\SYSTEM\CurrentControlSet\Services\Eventlog\" & $__g_sSourceName_Event & "\" & $sSource
    Local $aMsgDLL = StringSplit(_WinAPI_ExpandEnvironmentStrings(RegRead($sKey, "EventMessageFile")), ";")


Local Const $__EVENTLOG_LOAD_LIBRARY_AS_DATAFILE = 2 ;         ..is a global but you may need it while testing
Local Const $__EVENTLOG_FORMAT_MESSAGE_IGNORE_INSERTS = 512 ;  ..is a global but you may need it while testing
Local Const $__EVENTLOG_FORMAT_MESSAGE_FROM_HMODULE = 2048 ;   ..is a global but you may need it while testing


    Local $iFlags = BitOR($__EVENTLOG_FORMAT_MESSAGE_FROM_HMODULE, $__EVENTLOG_FORMAT_MESSAGE_IGNORE_INSERTS)
    Local $sDesc = ""
    Local $tBuffer = 0
    For $iI = 1 To $aMsgDLL[0]
        Local $hDLL = _WinAPI_LoadLibraryEx($aMsgDLL[$iI], $__EVENTLOG_LOAD_LIBRARY_AS_DATAFILE)
        If $hDLL = 0 Then ContinueLoop
        $tBuffer = DllStructCreate("wchar Text[4096]")
        _WinAPI_FormatMessage($iFlags, $hDLL, $iEventID, 0, $tBuffer, 4096, 0)
        _WinAPI_FreeLibrary($hDLL)
        $sDesc &= DllStructGetData($tBuffer, "Text")
    Next
    $sKey = "HKLM\SYSTEM\CurrentControlSet\Services\Eventlog\" & $__g_sSourceName_Event & "\" & $__g_sSourceName_Event
    $aMsgDLL = StringSplit(_WinAPI_ExpandEnvironmentStrings(RegRead($sKey, "ParameterMessageFile")), ";")

    For $iI = 1 To $aMsgDLL[0]
        $hDLL = _WinAPI_LoadLibraryEx($aMsgDLL[$iI], $__EVENTLOG_LOAD_LIBRARY_AS_DATAFILE)
        If $hDLL <> 0 Then
            For $iJ = 1 To $aStrings[0] ;Added to parse secondary replacements
                $tBuffer = DllStructCreate("wchar Text[4096]")
                If StringLeft($aStrings[$iJ], 2) == "%%" Then
                    _WinAPI_FormatMessage($iFlags, $hDLL, Int(StringTrimLeft($aStrings[$iJ], 2)), 0, $tBuffer, 4096, 0)
                    If Not @error Then
                        $aStrings[$iJ] = DllStructGetData($tBuffer, "Text")
                    EndIf
                EndIf
            Next
            _WinAPI_FreeLibrary($hDLL)
        EndIf
    Next
    If $sDesc = "" Then
        For $iI = 1 To $aStrings[0]
            $sDesc &= $aStrings[$iI]
        Next
    Else
        For $iI = $aStrings[0] To 1 Step -1 ; this here needs fixing
;~          $sDesc = StringRegExpReplace($sDesc, ("(%" & $iI & ")(\R|\Z)"), StringReplace($aStrings[$iI], "\", "\\") & "$2")
            $sDesc = StringReplace($sDesc, "%" & $iI, $aStrings[$iI])
        Next
    EndIf
    Return StringStripWS($sDesc, $STR_STRIPLEADING + $STR_STRIPTRAILING)
EndFunc   ;==>__EventLog_DecodeDesc_3

 

Edited by argumentum
=)

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...