Write to Windows event log
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
Write script status to the Windows Event Log
AutoHotkey
<lang autohotkey>; By ABCza, http://www.autohotkey.com/board/topic/76170-function-send-windows-log-events/ h := RegisterForEvents("AutoHotkey") SendWinLogEvent(h, "Test Message") DeregisterForEvents(h)
/*
FUNCTION: SendWinLogEvent
Writes an entry at the end of the specified Windows event log. Returns nonzero if the function succeeds or zero if it fails.
PARAMETERS: ~~~~~~~~~~~ hSource - Handle to a previously registered events source with RegisterForEvents. evType - EVENTLOG_SUCCESS := 0x0000 EVENTLOG_AUDIT_FAILURE := 0x0010 EVENTLOG_AUDIT_SUCCESS := 0x0008 EVENTLOG_ERROR_TYPE := 0x0001 EVENTLOG_INFORMATION_TYPE := 0x0004 EVENTLOG_WARNING_TYPE := 0x0002 evId - Event ID, can be any dword value. evCat - Any value, used to organize events in categories. pStrings - A continuation section with newline separated strings (each max 31839 chars). pData - A buffer containing the binary data.
SYSTEM CALLS, STRUCTURES AND INFO:
ReportEvent - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx Event Identifiers - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363651(v=vs.85).aspx Event categories - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363649(v=vs.85).aspx
- /
SendWinLogEvent(hSource, String="", evType=0x0004, evId=0x03EA, evCat=0, pData=0) { Ptr := A_PtrSize ? "Ptr" : "UInt" StringPut := A_IsUnicode ? "StrPut" : "StrPut2"
; Reserve and initialise space for the event message. VarSetCapacity(eventMessage, StrLen(String), 0) %StringPut%(String, eventMessage)
r := DllCall("Advapi32.dll\ReportEvent" (A_IsUnicode ? "W" : "A") , UInt, hSource ; handle , UShort, evType ; WORD, eventlog_information_type , UShort, evCat ; WORD, category , UInt, evId ; DWORD, event ID, 0x03EA , Ptr, 0 ; PSID, ptr to user security ID , UShort, 1 ; WORD, number of strings , UInt, VarSetCapacity(pData) ; DWORD, data size , Ptr, &eventMessage ; LPCTSTR*, ptr to a buffer ... , Ptr, (VarSetCapacity(pData)) ? &pData : 0 ) ; ptr to a buffer of binary data
; Release memory. VarSetCapacity(eventMessage, 0)
Return r } /*
FUNCTION: RegisterForEvents
Registers the application to send Windows log events. Returns a handle to the registered source.
PARAMETERS: ~~~~~~~~~~~ logName - Can be "Application", "System" or a custom log name.
SYSTEM CALLS, STRUCTURES AND INFO:
RegisterEventSource - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363678(v=VS.85).aspx Event Sources - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363661(v=VS.85).aspx
- /
RegisterForEvents(logName) { Return DllCall("Advapi32.dll\RegisterEventSource" (A_IsUnicode ? "W" : "A") , UInt, 0 ; LPCTSTR, Local computer , Str, logName) ; LPCTSTR Source name } /*
FUNCTION: DeregisterForEvents
Deregisters the previously registered application.
PARAMETERS: ~~~~~~~~~~~ hSource - Handle to a registered source.
SYSTEM CALLS, STRUCTURES AND INFO:
DeregisterEventSource - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363642(v=vs.85).aspx Event Sources - http://msdn.microsoft.com/en-us/library/windows/desktop/aa363661(v=VS.85).aspx
- /
DeregisterForEvents(hSource) { IfNotEqual, hSource, 0, Return DllCall( "Advapi32.dll\DeregisterEventSource" , UInt, hSource ) }
- StrPut for AutoHotkey Basic
StrPut2(String, Address="", Length=-1, Encoding=0) { ; Flexible parameter handling: if Address is not integer ; StrPut(String [, Encoding]) Encoding := Address, Length := 0, Address := 1024 else if Length is not integer ; StrPut(String, Address, Encoding) Encoding := Length, Length := -1
; Check for obvious errors. if (Address+0 < 1024) return
; Ensure 'Encoding' contains a numeric identifier. if Encoding = UTF-16 Encoding = 1200 else if Encoding = UTF-8 Encoding = 65001 else if SubStr(Encoding,1,2)="CP" Encoding := SubStr(Encoding,3)
if !Encoding ; "" or 0 { ; No conversion required. char_count := StrLen(String) + 1 ; + 1 because generally a null-terminator is wanted. if (Length) { ; Check for sufficient buffer space. if (StrLen(String) <= Length || Length == -1) { if (StrLen(String) == Length) ; Exceptional case: caller doesn't want a null-terminator. char_count-- ; Copy the string, including null-terminator if requested. DllCall("RtlMoveMemory", "uint", Address, "uint", &String, "uint", char_count) } else ; For consistency with the sections below, don't truncate the string. char_count = 0 } ;else: Caller just wants the the required buffer size (char_count), which will be returned below. } else if Encoding = 1200 ; UTF-16 { ; See the 'else' to this 'if' below for comments. if (Length <= 0) { char_count := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, "uint", &String, "int", StrLen(String), "uint", 0, "int", 0) + 1 if (Length == 0) return char_count Length := char_count } char_count := DllCall("MultiByteToWideChar", "uint", 0, "uint", 0, "uint", &String, "int", StrLen(String), "uint", Address, "int", Length) if (char_count && char_count < Length) NumPut(0, Address+0, char_count++*2, "UShort") } else if Encoding is integer { ; Convert native ANSI string to UTF-16 first. NOTE - wbuf_len includes the null-terminator. VarSetCapacity(wbuf, 2 * wbuf_len := StrPut2(String, "UTF-16")), StrPut2(String, &wbuf, "UTF-16")
; UTF-8 and some other encodings do not support this flag. Avoid it for UTF-8 ; (which is probably common) and rely on the fallback behaviour for other encodings. flags := Encoding=65001 ? 0 : 0x400 ; WC_NO_BEST_FIT_CHARS if (Length <= 0) ; -1 or 0 { ; Determine required buffer size. loop 2 { char_count := DllCall("WideCharToMultiByte", "uint", Encoding, "uint", flags, "uint", &wbuf, "int", wbuf_len, "uint", 0, "int", 0, "uint", 0, "uint", 0) if (char_count || A_LastError != 1004) ; ERROR_INVALID_FLAGS break flags := 0 ; Try again without WC_NO_BEST_FIT_CHARS. } if (!char_count) return ; FAIL if (Length == 0) ; Caller just wants the required buffer size. return char_count ; Assume there is sufficient buffer space and hope for the best: Length := char_count } ; Convert to target encoding. char_count := DllCall("WideCharToMultiByte", "uint", Encoding, "uint", flags, "uint", &wbuf, "int", wbuf_len, "uint", Address, "int", Length, "uint", 0, "uint", 0) ; Since above did not null-terminate, check for buffer space and null-terminate if there's room. ; It is tempting to always null-terminate (potentially replacing the last byte of data), ; but that would exclude this function as a means to copy a string into a fixed-length array. if (char_count && char_count < Length) NumPut(0, Address+0, char_count++, "Char") ; else no space to null-terminate; or conversion failed. } ; Return the number of characters copied. return char_count }</lang>
Batch File
The "EventCreate" command does the task. <lang dos>@echo off EventCreate /t ERROR /id 123 /l SYSTEM /so "A Batch File" /d "This is found in system log." EventCreate /t WARNING /id 456 /l APPLICATION /so BlaBla /d "This is found in apps log"</lang>
- Output:
>EventLog.BAT SUCCESS: An event of type 'ERROR' was created in the 'SYSTEM' log with 'A Batch File' as the source. SUCCESS: An event of type 'WARNING' was created in the 'APPLICATION' log with 'BlaBla' as the source. >
If you do not want the command to display its result or errors... <lang dos>@echo off EventCreate /t ERROR /id 123 /l SYSTEM /so "A Batch File" /d "This is found in system log." 2>NUL>NUL EventCreate /t WARNING /id 456 /l APPLICATION /so BlaBla /d "This is found in apps log" 2>NUL>NUL
- That "2>NUL>NUL" trick actually works in any command!</lang>
NOTE: This will (...or might) not work if you do not have administrator privileges.
BBC BASIC
Writes to the Application Log: <lang bbcbasic> INSTALL @lib$+"COMLIB"
PROC_cominitlcid(1033) WshShell% = FN_createobject("WScript.Shell") PROC_callmethod(WshShell%, "LogEvent(0, ""Test from BBC BASIC"")") PROC_releaseobject(WshShell%) PROC_comexit</lang>
C
The following is a wrapper on the EventCreate utility provided in Windows. Note that to use this wrapper, the code must be executed from a console/IDE running as Administrator. The utility itself does extensive error-checking and validation, so apart from the check that 5 arguments have been supplied, no other validations or checks are performed. <lang C> /*Abhishek Ghosh, 6th October 2017*/
- include<stdlib.h>
- include<stdio.h>
int main(int argC,char* argV[]) { char str[1000];
if(argC!=5) printf("Usage : %s < Followed by level, id, source string and description>",argV[0]); else{ sprintf(str,"EventCreate /t %s /id %s /l APPLICATION /so %s /d \"%s\"",argV[1],argV[2],argV[3],argV[4]); system(str); }
return 0; } </lang> Invocation and output on console :
C:\rosettaCode>eventLog.exe WARNING 458 SOmeString "This is a joke" SUCCESS: An event of type 'WARNING' was created in the 'APPLICATION' log with 'SOmeString' as the source.
Microsoft does provide an C/C++ API for EventCreate, but as with everything Microsoft, it's so wonderfully convoluted, that I will just give a link to the ReportEvent example.
C#
In Windows Vista and later or Windows Server 2003, you must have administrative privileges to execute this code. <lang csharp>using System.Diagnostics;
namespace RC {
internal class Program { public static void Main() { string sSource = "Sample App"; string sLog = "Application"; string sEvent = "Hello from RC!";
if (!EventLog.SourceExists(sSource)) EventLog.CreateEventSource(sSource, sLog);
EventLog.WriteEntry(sSource, sEvent); EventLog.WriteEntry(sSource, sEvent, EventLogEntryType.Information); } }
}</lang>
Clojure
<lang clojure>(use 'clojure.java.shell) (sh "eventcreate" "/T" "INFORMATION" "/ID" "123" "/D" "Rosetta Code example")</lang>
Delphi
<lang Delphi>program WriteToEventLog;
{$APPTYPE CONSOLE}
uses Windows;
procedure WriteLog(aMsg: string); var
lHandle: THandle; lMessagePtr: Pointer;
begin
lMessagePtr := PChar(aMsg); lHandle := RegisterEventSource(nil, 'Logger'); if lHandle > 0 then begin try ReportEvent(lHandle, 4 {Information}, 0, 0, nil, 1, 0, @lMessagePtr, nil); finally DeregisterEventSource(lHandle); end; end;
end;
begin
WriteLog('Message to log.');
end.</lang>
F#
Bare bone writing to the Application Eventlog giving no event-ID and using the default event type (information.)
<lang fsharp>use log = new System.Diagnostics.EventLog() log.Source <- "Sample Application" log.WriteEntry("Entered something in the Application Eventlog!")</lang>
Kotlin
The following works on Windows 10 with administrative privileges: <lang scala>// version 1.1.4-3
fun main(args: Array<String>) {
val command = "EventCreate" + " /t INFORMATION" + " /id 123" + " /l APPLICATION" + " /so Kotlin" + " /d \"Rosetta Code Example\""
Runtime.getRuntime().exec(command)
}</lang>
PicoLisp
PicoLisp doesn't run on Windows. In case of Linux, the equivalent of the event log is the syslog. It can be written with 'native' C functions, or simply with the 'logger' utility: <lang PicoLisp>: (call 'logger "This is a test") -> T
- (call 'logger "This" 'is "another" 'test)
-> T</lang>
PureBasic
<lang PureBasic>Procedure WriteToLog(Event_App$,EventMessage$,EvenetType,Computer$)
Protected wNumStrings.w, lpString=@EventMessage$, lReturnX, CMessageTyp, lparray Protected lprawdata=@EventMessage$, rawdata=Len(EventMessage$), Result Protected lLogAPIRetVal.l = RegisterEventSource_(Computer$, Event_App$)
If lLogAPIRetVal lReturnX = ReportEvent_(lLogAPIRetVal,EvenetType,0,CMessageTyp,0,wNumStrings,rawdata,lparray,lprawdata DeregisterEventSource_(lLogAPIRetVal) Result=#True EndIf
ProcedureReturn Result
EndProcedure</lang>
PowerShell
<lang powershell># Create Event Log object $EventLog=new-object System.Diagnostics.EventLog("Application")
- Declare Event Source; must be 'registered' with Windows
$EventLog.Source="Application" # It is possible to register a new source (see Note2)
- Setup the Event Types; you don't have to use them all, but I'm including all the possibilities for reference
$infoEvent=[System.Diagnostics.EventLogEntryType]::Information $errorEvent=[System.Diagnostics.EventLogEntryType]::Error $warningEvent=[System.Diagnostics.EventLogEntryType]::Warning $successAuditEvent=[System.Diagnostics.EventLogEntryType]::SuccessAudit $failureAuditEvent=[System.Diagnostics.EventLogEntryType]::FailureAudit
- Write the event in the format "Event test",EventType,EventID
$EventLog.WriteEntry("My Test Event",$infoevent,70)</lang>
Note1: Thanks to PoSH Fan for posting information that got me started on this at Windows PowerShell Blog
Note2: See details on registering a new Event Source with Windows at MSDN
Python
<lang Python>import win32api import win32con import win32evtlog import win32security import win32evtlogutil
ph = win32api.GetCurrentProcess() th = win32security.OpenProcessToken(ph, win32con.TOKEN_READ) my_sid = win32security.GetTokenInformation(th, win32security.TokenUser)[0]
applicationName = "My Application" eventID = 1 category = 5 # Shell myType = win32evtlog.EVENTLOG_WARNING_TYPE descr = ["A warning", "An even more dire warning"] data = "Application\0Data".encode("ascii")
win32evtlogutil.ReportEvent(applicationName, eventID, eventCategory=category, eventType=myType, strings=descr, data=data, sid=my_sid)</lang>
Racket
Racket's logging facility creates windows events when running on Windows.
<lang Racket>
- lang racket
(log-warning "Warning: nothing went wrong.") </lang>
REXX
This was executed on a (Microsoft) Windows/XP PRO system.
annotated
<lang rexx>/*REXX program writes a "record" (event) to the (Microsoft) Windows event log. */
eCMD = 'EVENTCREATE' /*name of the command that'll be used. */ type = 'INFORMATION' /*one of: ERROR WARNING INFORMATION */ id = 234 /*in range: 1 ───► 1000 (inclusive).*/ logName = 'APPLICATION' /*information about what this is. */ source = 'REXX' /* " " who's doing this. */ desc = 'attempting to add an entry for a Rosetta Code demonstration.' desc = '"' || desc || '"' /*enclose description in double quotes.*/
eCMD '/T' type "/ID" id '/L' logName "/SO" source '/D' desc
/*stick a fork in it, we're all done. */</lang>
- output:
SUCCESS: A 'INFORMATION' type event is created in the 'REXX' log/source.
bare bones
<lang rexx>/*REXX program writes a "record" (event) to the (Microsoft) Windows event log. */
/* [↓] cmd options have extra spacing.*/
'EVENTCREATE /T INFORMATION /ID 234 /L APPLICATION /SO REXX' ,
'/D "attempting to add an entry for a Rosetta Code demonstration."'
/*stick a fork in it, we're all done. */</lang>
- output is identical to the 1st REXX version.
Ruby
<lang ruby>require 'win32/eventlog' logger = Win32::EventLog.new logger.report_event(:event_type => Win32::EventLog::INFO, :data => "a test event log entry")</lang>
Instructions on setting up an Event Source is here
Tcl
<lang tcl>package require twapi
- This command handles everything; use “-type error” to write an error message
twapi::eventlog_log "My Test Event" -type info</lang>
VBScript
<lang vb> Sub write_event(event_type,msg) Set objShell = CreateObject("WScript.Shell") Select Case event_type Case "SUCCESS" n = 0 Case "ERROR" n = 1 Case "WARNING" n = 2 Case "INFORMATION" n = 4 Case "AUDIT_SUCCESS" n = 8 Case "AUDIT_FAILURE" n = 16 End Select objShell.LogEvent n, msg Set objShell = Nothing End Sub
Call write_event("INFORMATION","This is a test information.") </lang>
zkl
<lang zkl>zkl: System.cmd(0'|eventcreate "/T" "INFORMATION" "/ID" "123" "/D" "Rosetta Code example"|)</lang>
- Output:
SUCCESS: A 'INFORMATION' type event is created in the 'EventCreate' log/source. 0
- Programming Tasks
- Solutions by Programming Task
- AutoHotkey
- Batch File
- BBC BASIC
- C
- C sharp
- Clojure
- Delphi
- F Sharp
- Kotlin
- PicoLisp
- PureBasic
- PowerShell
- Python
- PyWin32
- Racket
- REXX
- Ruby
- Win32-utils
- Tcl
- TWAPI
- VBScript
- Zkl
- Lotus 123 Macro Scripting/Omit
- Mathematica/Omit
- Maxima/Omit
- PARI/GP/Omit
- PostScript/Omit
- Retro/Omit
- Swift/Omit
- TI-83 BASIC/Omit
- Zkl/Omit
- ZX Spectrum Basic/Omit