Search code examples
vbscriptevent-logfilesystemobject

Win32_NTLogEvent message property FilSystemObject.Write procedure call issue


I am writing a script to write event log information to a csv. My script was working before. But I changed some stuff, which shouldn't have any effect on writing to the csv. I have tried writing to unicode and to ASCII, based on some research I did on the internet about this issue. Neither makes a difference. The odd thing is that I use the same code later in my script to write other logs (I first write system logs, then I write application logs, etc.), and it works perfectly there. The code I am using is temporary, as I have not got around to writing a way to delete carriage returns from messages (which causes issues with importing the CSV to Excel). So it might fix itself once I do that. But it seems like it is a larger issue than that. Here is the script up until it moves on to other logs:

Set wshShell = WScript.CreateObject( "WScript.Shell" )
strComputerName = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" )

strComputer = "."
strType = "Error"
strPath = "T:\IT resources\Event Logs\ErrorLog" & strComputerName & ".csv"

'Script to convert UTC to human readable. From Script Repository.

Function WMIDateStringToDate(dtmInstallDate)
    WMIDateStringToDate = CDate(Mid(dtmInstallDate, 5, 2) & "/" & _
        Mid(dtmInstallDate, 7, 2) & "/" & Left(dtmInstallDate, 4) _
            & " " & Mid (dtmInstallDate, 9, 2) & ":" & _
                Mid(dtmInstallDate, 11, 2) & ":" & Mid(dtmInstallDate, _
                    13, 2))
End Function

'ForWriting is to write to file from start. ForAppending is to write to file from end of file.

constForWriting = 2
constForAppending = 8
constTristate = 0
boolUnicode = False
chrCarriageReturn = chr(13)
chrNewLine = chr(10)

Set objFSO = CreateObject("Scripting.FileSystemObject")


'This is so that cscript won't encounter a runtime error if the file already exists. Also so that it will write to the already existing file.

If objFSO.FileExists(strPath)=False Then

 Set objErrLog = objFSO.CreateTextFile(strPath,constForWriting,boolUnicode)
  objErrLog.Write "Type,"
  objErrLog.Write "Time Generated,"
  objErrLog.Write "Source Name,"
  objErrLog.Write "Event Code,"
  objErrLog.Write "Category,"
  objErrLog.Write "Message"
  objErrLog.Writeline
 strTimeMin = "01/01/1970/0:00:00"
'19700101000000.000000-480

 Else Set objErrLog = objFSO.OpenTextFile(strPath,constForAppending,constTristate)

'Only need this if it writes from the line the file ends on, as opposed to starting on a new line (which I expect it will).

  objErrLog.WriteLine

End If

Set objWMIService = GetObject("winmgmts:" _
 & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")

'Querying Event Logs

Set colLoggedEvents = objWMIService.ExecQuery _
 ("SELECT * FROM Win32_NTLogEvent WHERE Logfile = 'system' AND "_
 & "Type = 'Error'")

'Type='Error' instead of "1" because it is a WQL query, I think. I believe that it is searching the entries in a database that reference the Win32_NTLogEvent objects. So I am searching the values in the database as opposed to the properties of the objects they reference. Or perhaps not. WHen I echo the type property of every object in colLoggedEvents, cscript outputs "Error". So maybe the I'm reading the SDK wrong? At least it seems to be working.

'This is a comparison function which tells where string 2 occurs in string 1. Starts at 1.

constStart = 1
constCompareType = 0

'This loop writes the information to a .csv.

For Each objEvent In colLoggedEvents

 If objEvent.Timegenerated > strTimeMin Then
  strTimeMin = objEvent.TimeGenerated
 Else
 End If
 objErrLog.Write objEvent.Type & ","
 objErrLog.Write WMIDateStringToDate(objEvent.TimeGenerated) & ","
 objErrLog.Write objEvent.SourceName & ","
 objErrLog.Write objEvent.EventCode & ","

 constExist=InStr(constStart,objEvent.Message,chrCarriageReturn,constCompareType)+InStr(constStart,objEvent.Message,chrNewLine,constCompareType)

  If constExist = 0 Then
   objErrLog.Write objEvent.Category & ","
   objErrLog.Write objEvent.Message

  Else
   objErrLog.Write objEvent.Category

  End If

 objErrLog.WriteLine

Next

Any help would be greatly appreciated.


Solution

    1. Loose the misconception that code 'might fix itself'
    2. Give the full error details (number, description, line identified) when asking a question
    3. Assuming that you got a "5 - Invalid procedure call or argument" error on a line starting with "objErrLog.Write" see here for an explanation.
    4. You claim you have tested a variant of your code using Unicode; you didn't, because:

    The prototype of .CreateTextFile is

    object.CreateTextFile(filename:string[, overwrite:bool[, unicode:bool]])
    

    This clashes with your

    objFSO.CreateTextFile(strPath,constForWriting,boolUnicode)
    

    The prototype of .OpenTextFile is

    object.OpenTextFile(filename:string[, iomode:enum[, create:bool[, format:enum]]])
    

    This clashes with your

    objFSO.OpenTextFile(strPath,constForAppending,constTristate)
    

    So fix these blunders (yourself!), test with the file really opened for Unicode, and hope that assumption (3) holds.

    Update wrt comments:

    Please reflect upon "Give the full error details (number, description, line identified) when asking a question" in the context of:

    I get an invalid procedure error after 68 members of colLoggedEvents when I have the file in ASCII.

    vs

    I get the error when I call the OpenTextFile method

    The first statement implies that the 68th member contains characters that can't be written in ASCII/ANSI mode. Really/Correctly using Unicode output format will fix the problem, provided the error log does not contain invalid data.

    The second statement indicates that the parameters to the .Open/CreateTextfile methods are still not correct. Did you change both invocations to Unicode?

    Update II:

    The docs define

    TristateTrue -1 Opens the file as Unicode.

    Your code wrongly uses:

    constTristate = 1 
    Set objErrLog = objFSO.OpenTextFile(strPath,constForAppending,boolCreate,constTristate) 
    

    Evidence:

    >> Set ts = goFS.OpenTextFile("error5.txt", 8, False, 1)
    >>
    Error Number:       5
    Error Description:  Invalid procedure call or argument
    >> Set ts = goFS.OpenTextFile("error5.txt", 8, False, -1)
    >>
    >> <-- no news are good news
    

    Update wrt comment concerning TriStateTrue:

    The VBScript doc say:

    TristateUseDefault -2 Opens the file using the system default.
    TristateTrue       -1 Opens the file as Unicode.
    TristateFalse       0 Opens the file as ASCII.
    

    The doc @Adam refered to concerns VBA; but I wouldn't trust it without a check.