diff --git a/README.md b/README.md index d40ef21..7e93332 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ # gowinlog Go library for subscribing to the Windows Event Log. +Godocs +======= +[gowinlog v0](https://gopkg.in/scalingdata/gowinlog.v0)) + Installation ======= -gowinlog uses cgo, so it needs gcc, and `evt.h` must be available. Installing [MinGW-w64](http://mingw-w64.yaxm.org/doku.php) should satisfy both requirements. Make sure the Go architecture and GCC architecture are the same. +gowinlog uses cgo, so it needs gcc. Installing [MinGW-w64](http://mingw-w64.yaxm.org/doku.php) should satisfy both requirements. Make sure the Go architecture and GCC architecture are the same. + +Features +======== + +- Includes wrapper for wevtapi.dll, and a high level API +- Supports bookmarks for resuming consumption +- Filter events using XPath expressions Usage ======= -In Go, create a watcher and subscribe to some log channels. Subscriptions can start at the beginning of the log, at the most recent event, or at a bookmark. Events and errors are coerced into Go structs and published on the `Event()` and `Error()` channels. Every event includes a `Bookmark` field which can be stored and used to resume processing at the same point. - ``` Go package main @@ -25,15 +34,14 @@ func main() { fmt.Printf("Couldn't create watcher: %v\n", err) return } - // Recieve any future messages - watcher.SubscribeFromNow("Application") + // Recieve any future messages on the Application channel + // "*" doesn't filter by any fields of the event + watcher.SubscribeFromNow("Application", "*") for { select { case evt := <- watcher.Event(): // Print the event struct fmt.Printf("Event: %v\n", evt) - // Print the updated bookmark for that channel - fmt.Printf("Bookmark XML: %v\n", evt.Bookmark) case err := <- watcher.Error(): fmt.Printf("Error: %v\n\n", err) } @@ -44,4 +52,4 @@ func main() { Low-level API ------ -`event.go` contains wrappers around the C events API. `bookmark.go` has wrappers around the bookmark API. +`winevt.go` provides wrappers around the relevant functions in `wevtapi.dll`. diff --git a/bookmark.c b/bookmark.c deleted file mode 100644 index 87faba0..0000000 --- a/bookmark.c +++ /dev/null @@ -1,55 +0,0 @@ -// +build windows - -#include "bookmark.h" - -ULONGLONG CreateBookmark() { - return (ULONGLONG)EvtCreateBookmark(NULL); -} - -ULONGLONG CreateBookmarkFromXML(char* xmlString) { - size_t xmlWideLen= mbstowcs(NULL, xmlString, 0) + 1; - LPWSTR lxmlString = malloc(xmlWideLen * sizeof(wchar_t)); - if (!lxmlString) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - // Convert Go string to wide characters - mbstowcs(lxmlString, xmlString, xmlWideLen); - return (ULONGLONG)EvtCreateBookmark(lxmlString); -} - -int UpdateBookmark(ULONGLONG hBookmark, ULONGLONG hEvent) { - return EvtUpdateBookmark((EVT_HANDLE)hBookmark, (EVT_HANDLE)hEvent); -} - -char* RenderBookmark(ULONGLONG hBookmark) { - DWORD dwUsed; - DWORD dwProps; - DWORD dwSize = 0; - EvtRender(NULL, (EVT_HANDLE)hBookmark, EvtRenderBookmark, dwSize, NULL, &dwUsed, &dwProps); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER){ - return NULL; - } - dwSize = dwUsed + 1; - LPWSTR xmlWide = malloc((dwSize) * sizeof(wchar_t)); - if (!xmlWide) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - int renderResult = EvtRender(NULL, (EVT_HANDLE)hBookmark, EvtRenderBookmark, dwSize, xmlWide, &dwUsed, &dwProps); - if (!renderResult) { - free(xmlWide); - return 0; - } - size_t xmlNarrowLen = wcstombs(NULL, xmlWide, 0) + 1; - void* xmlNarrow = malloc(xmlNarrowLen); - if (!xmlNarrow) { - free(xmlWide); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - wcstombs(xmlNarrow, xmlWide, xmlNarrowLen); - free(xmlWide); - return xmlNarrow; -} diff --git a/bookmark.go b/bookmark.go index f98b0cd..5c59013 100644 --- a/bookmark.go +++ b/bookmark.go @@ -2,46 +2,46 @@ package winlog -/* -#cgo LDFLAGS: -l wevtapi -#include "bookmark.h" -*/ -import "C" import ( - "unsafe" + "syscall" ) +/* Create a new, empty bookmark. Bookmark handles must be closed with CloseEventHandle. */ func CreateBookmark() (BookmarkHandle, error) { - bookmark := BookmarkHandle(C.CreateBookmark()) - if bookmark == 0 { - return 0, GetLastError() + bookmark, err := EvtCreateBookmark(nil) + if err != nil { + return 0, err } - return bookmark, nil + return BookmarkHandle(bookmark), nil } +/* Create a bookmark from a XML-serialized bookmark. Bookmark handles must be closed with CloseEventHandle. */ func CreateBookmarkFromXml(xmlString string) (BookmarkHandle, error) { - cString := C.CString(xmlString) - bookmark := C.CreateBookmarkFromXML(cString) - C.free(unsafe.Pointer(cString)) + wideXmlString, err := syscall.UTF16PtrFromString(xmlString) + if err != nil { + return 0, err + } + bookmark, err := EvtCreateBookmark(wideXmlString) if bookmark == 0 { - return 0, GetLastError() + return 0, err } return BookmarkHandle(bookmark), nil } +/* Update a bookmark to store the channel and ID of the given event */ func UpdateBookmark(bookmarkHandle BookmarkHandle, eventHandle EventHandle) error { - if C.UpdateBookmark(C.ULONGLONG(bookmarkHandle), C.ULONGLONG(eventHandle)) == 0 { - return GetLastError() - } - return nil + return EvtUpdateBookmark(syscall.Handle(bookmarkHandle), syscall.Handle(eventHandle)) } +/* Serialize the bookmark as XML */ func RenderBookmark(bookmarkHandle BookmarkHandle) (string, error) { - cString := C.RenderBookmark(C.ULONGLONG(bookmarkHandle)) - if cString == nil { - return "", GetLastError() + var dwUsed uint32 + var dwProps uint32 + EvtRender(0, syscall.Handle(bookmarkHandle), EvtRenderBookmark, 0, nil, &dwUsed, &dwProps) + buf := make([]uint16, dwUsed) + err := EvtRender(0, syscall.Handle(bookmarkHandle), EvtRenderBookmark, uint32(len(buf)), &buf[0], &dwUsed, &dwProps) + if err != nil { + return "", err } - bookmarkXml := C.GoString(cString) - C.free(unsafe.Pointer(cString)) - return bookmarkXml, nil + return syscall.UTF16ToString(buf), nil } diff --git a/bookmark.h b/bookmark.h deleted file mode 100644 index f90939f..0000000 --- a/bookmark.h +++ /dev/null @@ -1,16 +0,0 @@ -#define _WIN32_WINNT 0x0602 - -#include -#include "winevt.h" -#include -#include -#include - -// Make a new, empty bookmark to update -ULONGLONG CreateBookmark(); -// Load an existing bookmark from a Go XML string -ULONGLONG CreateBookmarkFromXML(char* xmlString); -// Update a bookmark to the given event handle -int UpdateBookmark(ULONGLONG hBookmark, ULONGLONG hEvent); -// Render the XML string for a bookmark -char* RenderBookmark(ULONGLONG hBookmark); diff --git a/bookmark_test.go b/bookmark_test.go index 856bd45..4e0fafd 100644 --- a/bookmark_test.go +++ b/bookmark_test.go @@ -5,7 +5,6 @@ package winlog import ( "encoding/xml" . "testing" - "unsafe" ) type bookmarkListXml struct { @@ -85,9 +84,8 @@ func TestUpdateBookmark(t *T) { if err != nil { t.Fatal(err) } - defer Free(unsafe.Pointer(renderedFields)) - channel, _ := RenderStringField(renderedFields, EvtSystemChannel) - eventId, _ := RenderUIntField(renderedFields, EvtSystemEventRecordId) + channel, _ := renderedFields.String(EvtSystemChannel) + eventId, _ := renderedFields.Uint(EvtSystemEventRecordId) bookmarkChannel := bookmarkStruct.Bookmarks[0].Channel bookmarkId := bookmarkStruct.Bookmarks[0].RecordId diff --git a/event.c b/event.c deleted file mode 100644 index 492eeaf..0000000 --- a/event.c +++ /dev/null @@ -1,211 +0,0 @@ -// +build windows - -#define _WIN32_WINNT 0x0602 - -#include "event.h" -#include "_cgo_export.h" - -int CloseEvtHandle(ULONGLONG hEvent) { - EvtClose((EVT_HANDLE)hEvent); -} - -int CancelEvtHandle(ULONGLONG hEvent) { - EvtCancel((EVT_HANDLE)hEvent); -} - -PVOID RenderEventValues(ULONGLONG hContext, ULONGLONG hEvent) { - DWORD dwBufferSize = 0; - DWORD dwUsed = 0; - DWORD dwPropertyCount = 0; - EvtRender((EVT_HANDLE)hContext, (EVT_HANDLE)hEvent, EvtRenderEventValues, dwBufferSize, NULL, &dwUsed, &dwPropertyCount); - PVOID pRenderedValues = malloc(dwUsed); - if (!pRenderedValues) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - dwBufferSize = dwUsed; - if (! EvtRender((EVT_HANDLE)hContext, (EVT_HANDLE)hEvent, EvtRenderEventValues, dwBufferSize, pRenderedValues, &dwUsed, &dwPropertyCount)){ - free(pRenderedValues); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - return pRenderedValues; -} - -char* GetLastErrorString() { - DWORD dwErr = GetLastError(); - LPSTR lpszMsgBuf; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, 0, dwErr, 0, (LPSTR)&lpszMsgBuf, 0, NULL); - return (char *)lpszMsgBuf; -} - -char* GetFormattedMessage(ULONGLONG hEventPublisher, ULONGLONG hEvent, int format) { - DWORD dwBufferSize = 0; - DWORD dwBufferUsed = 0; - int status; - errno_t decodeReturn = EvtFormatMessage((EVT_HANDLE)hEventPublisher, (EVT_HANDLE)hEvent, 0, 0, NULL, format, 0, NULL, &dwBufferUsed); - if ((status = GetLastError()) != ERROR_INSUFFICIENT_BUFFER) { - return NULL; - } - dwBufferSize = dwBufferUsed + 1; - LPWSTR messageWide = malloc((dwBufferSize) * sizeof(wchar_t)); - if (!messageWide) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - decodeReturn = EvtFormatMessage((EVT_HANDLE)hEventPublisher, (EVT_HANDLE)hEvent, 0, 0, NULL, format, dwBufferSize, messageWide, &dwBufferUsed); - if (!decodeReturn) { - free(messageWide); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - size_t lenMessage = wcstombs(NULL, messageWide, 0) + 1; - void* message = malloc(lenMessage); - if (!message) { - free(messageWide); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - wcstombs(message, messageWide, lenMessage); - free(messageWide); - return message; -} - -ULONGLONG GetEventPublisherHandle(PVOID pRenderedValues) { - LPCWSTR publisher = ((PEVT_VARIANT)pRenderedValues)[EvtSystemProviderName].StringVal; - return (ULONGLONG)EvtOpenPublisherMetadata(NULL, publisher, NULL, 0, 0); -} - -ULONGLONG CreateSystemRenderContext() { - return (ULONGLONG)EvtCreateRenderContext(0, NULL, EvtRenderContextSystem); -} - -int GetRenderedValueType(PVOID pRenderedValues, int property) { - return (int)((PEVT_VARIANT)pRenderedValues)[property].Type; -} - -char* GetRenderedStringValue(PVOID pRenderedValues, int property) { - wchar_t const * propVal = ((PEVT_VARIANT)pRenderedValues)[property].StringVal; - size_t lenNarrowPropVal = wcstombs(NULL, propVal, 0) + 1; - char* value = malloc(lenNarrowPropVal); - if (!value) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - wcstombs(value, propVal, lenNarrowPropVal); - return value; -} - -ULONGLONG GetRenderedByteValue(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].ByteVal; -} - -ULONGLONG GetRenderedUInt16Value(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].UInt16Val; -} - -ULONGLONG GetRenderedUInt32Value(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].UInt32Val; -} - -ULONGLONG GetRenderedUInt64Value(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].UInt64Val; -} - -LONGLONG GetRenderedSByteValue(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].SByteVal; -} - -LONGLONG GetRenderedInt16Value(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].Int16Val; -} - -LONGLONG GetRenderedInt32Value(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].Int32Val; -} - -LONGLONG GetRenderedInt64Value(PVOID pRenderedValues, int property) { - return ((PEVT_VARIANT)pRenderedValues)[property].Int64Val; -} - -//FILETIME to unix epoch: https://support.microsoft.com/en-us/kb/167296 -ULONGLONG GetRenderedFileTimeValue(PVOID pRenderedValues, int property) { - FILETIME* ft = (FILETIME*) &(((PEVT_VARIANT)pRenderedValues)[property].FileTimeVal); - ULONGLONG time = ft->dwHighDateTime; - time = (time << 32) | ft->dwLowDateTime; - return (time / 10000000) - 11644473600; -} - -// Dispatch events and errors appropriately -DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent) -{ - switch(action) - { - case EvtSubscribeActionError: - eventCallbackError((ULONGLONG)hEvent, pContext); - break; - - case EvtSubscribeActionDeliver: - eventCallback((ULONGLONG)hEvent, pContext); - break; - - default: - // TODO: signal unknown error - eventCallbackError(0, pContext); - } - - return ERROR_SUCCESS; // The service ignores the returned status. -} - -ULONGLONG SetupListener(char* channel, char* query, PVOID pWatcher, EVT_HANDLE hBookmark, EVT_SUBSCRIBE_FLAGS flags) -{ - DWORD status = ERROR_SUCCESS; - EVT_HANDLE hSubscription = NULL; - size_t wideChannelLen; - size_t maxWideChannelLen = mbstowcs(NULL, channel, 0) + 1; - LPWSTR lChannel = malloc(maxWideChannelLen * sizeof(wchar_t)); - if (!lChannel) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - size_t maxWideQueryLen = mbstowcs(NULL, query, 0) + 1; - LPWSTR lQuery = malloc(maxWideQueryLen * sizeof(wchar_t)); - if (!lQuery) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - - // Convert Go string to wide characters - mbstowcs(lChannel, channel, maxWideChannelLen); - mbstowcs(lQuery, query, maxWideQueryLen); - - // Subscribe to events beginning in the present. All future events will trigger the callback. - hSubscription = EvtSubscribe(NULL, NULL, lChannel, lQuery, hBookmark, pWatcher, (EVT_SUBSCRIBE_CALLBACK)SubscriptionCallback, flags); - free(lChannel); - return (ULONGLONG)hSubscription; -} - -ULONGLONG CreateListener(char* channel, char* query, int startPos, PVOID pWatcher) { - return SetupListener(channel, query, pWatcher, NULL, startPos); -} - -ULONGLONG CreateListenerFromBookmark(char* channel, char* query, PVOID pWatcher, ULONGLONG hBookmark) { - return SetupListener(channel, query, pWatcher, (EVT_HANDLE)hBookmark, EvtSubscribeStartAfterBookmark); -} - -ULONGLONG GetTestEventHandle() { - DWORD status = ERROR_SUCCESS; - EVT_HANDLE record = 0; - DWORD recordsReturned; - EVT_HANDLE result = EvtQuery(NULL, L"Application", L"*", EvtQueryChannelPath); - if (result == 0) { - return 0; - } - if (!EvtNext(result, 1, &record, 500, 0, &recordsReturned)) { - EvtClose(result); - return 0; - } - EvtClose(result); - return (ULONGLONG)record; -} diff --git a/event.go b/event.go index e6fa7f8..3045a3b 100644 --- a/event.go +++ b/event.go @@ -2,288 +2,162 @@ package winlog -/* -#cgo LDFLAGS: -l wevtapi -#include "event.h" -*/ import "C" + import ( - "errors" "fmt" - "time" + "syscall" "unsafe" ) -type EVT_SUBSCRIBE_FLAGS int - -const ( - _ = iota - EvtSubscribeToFutureEvents - EvtSubscribeStartAtOldestRecord - EvtSubscribeStartAfterBookmark -) - -type EVT_VARIANT_TYPE int - -const ( - EvtVarTypeNull = iota - EvtVarTypeString - EvtVarTypeAnsiString - EvtVarTypeSByte - EvtVarTypeByte - EvtVarTypeInt16 - EvtVarTypeUInt16 - EvtVarTypeInt32 - EvtVarTypeUInt32 - EvtVarTypeInt64 - EvtVarTypeUInt64 - EvtVarTypeSingle - EvtVarTypeDouble - EvtVarTypeBoolean - EvtVarTypeBinary - EvtVarTypeGuid - EvtVarTypeSizeT - EvtVarTypeFileTime - EvtVarTypeSysTime - EvtVarTypeSid - EvtVarTypeHexInt32 - EvtVarTypeHexInt64 - EvtVarTypeEvtHandle - EvtVarTypeEvtXml -) - -/* Fields that can be rendered with GetRendered*Value */ -type EVT_SYSTEM_PROPERTY_ID int - -const ( - EvtSystemProviderName = iota - EvtSystemProviderGuid - EvtSystemEventID - EvtSystemQualifiers - EvtSystemLevel - EvtSystemTask - EvtSystemOpcode - EvtSystemKeywords - EvtSystemTimeCreated - EvtSystemEventRecordId - EvtSystemActivityID - EvtSystemRelatedActivityID - EvtSystemProcessID - EvtSystemThreadID - EvtSystemChannel - EvtSystemComputer - EvtSystemUserID - EvtSystemVersion -) - -/* Formatting modes for GetFormattedMessage */ -type EVT_FORMAT_MESSAGE_FLAGS int - -const ( - _ = iota - EvtFormatMessageEvent - EvtFormatMessageLevel - EvtFormatMessageTask - EvtFormatMessageOpcode - EvtFormatMessageKeyword - EvtFormatMessageChannel - EvtFormatMessageProvider - EvtFormatMessageId - EvtFormatMessageXml -) - -// Get a handle to a render context which will render properties from the System element. -// Wraps EvtCreateRenderContext() with Flags = EvtRenderContextSystem. The resulting -// handle must be closed with CloseEventHandle. +/* Get a handle to a render context which will render properties from the System element. + Wraps EvtCreateRenderContext() with Flags = EvtRenderContextSystem. The resulting + handle must be closed with CloseEventHandle. */ func GetSystemRenderContext() (SysRenderContext, error) { - context := SysRenderContext(C.CreateSystemRenderContext()) - if context == 0 { - return 0, GetLastError() + context, err := EvtCreateRenderContext(0, 0, EvtRenderContextSystem) + if err != nil { + return 0, err } - return context, nil + return SysRenderContext(context), nil } -// Get a handle for a event log subscription on the given channel. -// `query` is an XPath expression to filter the events on the channel - "*" allows all events. -// The resulting handle must be closed with CloseEventHandle. +/* Get a handle for a event log subscription on the given channel. + `query` is an XPath expression to filter the events on the channel - "*" allows all events. + The resulting handle must be closed with CloseEventHandle. */ func CreateListener(channel, query string, startpos EVT_SUBSCRIBE_FLAGS, watcher *LogEventCallbackWrapper) (ListenerHandle, error) { - cChan := C.CString(channel) - cQuery := C.CString(query) - listenerHandle := C.CreateListener(cChan, cQuery, C.int(startpos), C.PVOID(watcher)) - C.free(unsafe.Pointer(cChan)) - C.free(unsafe.Pointer(cQuery)) - if listenerHandle == 0 { - return 0, GetLastError() + wideChan, err := syscall.UTF16PtrFromString(channel) + if err != nil { + return 0, err + } + wideQuery, err := syscall.UTF16PtrFromString(query) + if err != nil { + return 0, err + } + listenerHandle, err := EvtSubscribe(0, 0, wideChan, wideQuery, 0, uintptr(unsafe.Pointer(watcher)), uintptr(syscall.NewCallback(eventCallback)), uint32(startpos)) + if err != nil { + return 0, err } return ListenerHandle(listenerHandle), nil } -// Get a handle for an event log subscription on the given channel. Will begin at the -// bookmarked event, or the closest possible event if the log has been truncated. -// `query` is an XPath expression to filter the events on the channel - "*" allows all events. -// The resulting handle must be closed with CloseEventHandle. +/* Get a handle for an event log subscription on the given channel. Will begin at the + bookmarked event, or the closest possible event if the log has been truncated. + `query` is an XPath expression to filter the events on the channel - "*" allows all events. + The resulting handle must be closed with CloseEventHandle. */ func CreateListenerFromBookmark(channel, query string, watcher *LogEventCallbackWrapper, bookmarkHandle BookmarkHandle) (ListenerHandle, error) { - cChan := C.CString(channel) - cQuery := C.CString(query) - listenerHandle := C.CreateListenerFromBookmark(cChan, cQuery, C.PVOID(watcher), C.ULONGLONG(bookmarkHandle)) - C.free(unsafe.Pointer(cChan)) - C.free(unsafe.Pointer(cQuery)) - if listenerHandle == 0 { - return 0, GetLastError() + wideChan, err := syscall.UTF16PtrFromString(channel) + if err != nil { + return 0, err } - return ListenerHandle(listenerHandle), nil -} - -// Get the Go string for the field at the given index. Returns -// false if the type of the field isn't EvtVarTypeString. -func RenderStringField(fields RenderedFields, fieldIndex EVT_SYSTEM_PROPERTY_ID) (string, bool) { - fieldType := C.GetRenderedValueType(C.PVOID(fields), C.int(fieldIndex)) - if fieldType != EvtVarTypeString { - return "", false + wideQuery, err := syscall.UTF16PtrFromString(query) + if err != nil { + return 0, err } - - cString := C.GetRenderedStringValue(C.PVOID(fields), C.int(fieldIndex)) - if cString == nil { - return "", false + listenerHandle, err := EvtSubscribe(0, 0, wideChan, wideQuery, syscall.Handle(bookmarkHandle), uintptr(unsafe.Pointer(watcher)), syscall.NewCallback(eventCallback), uint32(EvtSubscribeStartAfterBookmark)) + if err != nil { + return 0, err } - - value := C.GoString(cString) - C.free(unsafe.Pointer(cString)) - return value, true + return ListenerHandle(listenerHandle), nil } -// Get the timestamp of the field at the given index. Returns false if the -// type of the field isn't EvtVarTypeFileTime. -func RenderFileTimeField(fields RenderedFields, fieldIndex EVT_SYSTEM_PROPERTY_ID) (time.Time, bool) { - fieldType := C.GetRenderedValueType(C.PVOID(fields), C.int(fieldIndex)) - if fieldType != EvtVarTypeFileTime { - return time.Time{}, false +/* Get the formatted string that represents this message. This method wraps EvtFormatMessage. */ +func FormatMessage(eventPublisherHandle PublisherHandle, eventHandle EventHandle, format EVT_FORMAT_MESSAGE_FLAGS) (string, error) { + var size uint32 = 0 + err := EvtFormatMessage(syscall.Handle(eventPublisherHandle), syscall.Handle(eventHandle), 0, 0, nil, uint32(format), 0, nil, &size) + if err != nil { + if errno, ok := err.(syscall.Errno); !ok || errno != 122 { + // Check if the error is ERR_INSUFICIENT_BUFFER + return "", err + } } - field := C.GetRenderedFileTimeValue(C.PVOID(fields), C.int(fieldIndex)) - return time.Unix(int64(field), 0), true -} - -// Get the unsigned integer at the given index. Returns false if the field -// type isn't EvtVarTypeByte, EvtVarTypeUInt16, EvtVarTypeUInt32, or EvtVarTypeUInt64. -func RenderUIntField(fields RenderedFields, fieldIndex EVT_SYSTEM_PROPERTY_ID) (uint64, bool) { - var field C.ULONGLONG - fieldType := C.GetRenderedValueType(C.PVOID(fields), C.int(fieldIndex)) - switch fieldType { - case EvtVarTypeByte: - field = C.GetRenderedByteValue(C.PVOID(fields), C.int(fieldIndex)) - case EvtVarTypeUInt16: - field = C.GetRenderedUInt16Value(C.PVOID(fields), C.int(fieldIndex)) - case EvtVarTypeUInt32: - field = C.GetRenderedUInt32Value(C.PVOID(fields), C.int(fieldIndex)) - case EvtVarTypeUInt64: - field = C.GetRenderedUInt64Value(C.PVOID(fields), C.int(fieldIndex)) - default: - return 0, false + buf := make([]uint16, size) + err = EvtFormatMessage(syscall.Handle(eventPublisherHandle), syscall.Handle(eventHandle), 0, 0, nil, uint32(format), uint32(len(buf)), &buf[0], &size) + if err != nil { + return "", err } - - return uint64(field), true + return syscall.UTF16ToString(buf), nil } -// Get the signed integer at the given index. Returns false if the type of -// the field isn't EvtVarTypeSByte, EvtVarTypeInt16, EvtVarTypeInt32, EvtVarTypeInt64. -func RenderIntField(fields RenderedFields, fieldIndex EVT_SYSTEM_PROPERTY_ID) (int64, bool) { - var field C.LONGLONG - fieldType := C.GetRenderedValueType(C.PVOID(fields), C.int(fieldIndex)) - switch fieldType { - case EvtVarTypeSByte: - field = C.GetRenderedSByteValue(C.PVOID(fields), C.int(fieldIndex)) - case EvtVarTypeInt16: - field = C.GetRenderedInt16Value(C.PVOID(fields), C.int(fieldIndex)) - case EvtVarTypeInt32: - field = C.GetRenderedInt32Value(C.PVOID(fields), C.int(fieldIndex)) - case EvtVarTypeInt64: - field = C.GetRenderedInt64Value(C.PVOID(fields), C.int(fieldIndex)) - default: - return 0, false +/* Get the formatted string for the last error which occurred. Wraps GetLastError and FormatMessage. */ +func GetLastError() error { + return syscall.GetLastError() +} + +/* Render the system properties from the event and returns an array of properties. + Properties can be accessed using RenderStringField, RenderIntField, RenderFileTimeField, + or RenderUIntField depending on type. This buffer must be freed after use. */ +func RenderEventValues(renderContext SysRenderContext, eventHandle EventHandle) (EvtVariant, error) { + var bufferUsed uint32 = 0 + var propertyCount uint32 = 0 + err := EvtRender(syscall.Handle(renderContext), syscall.Handle(eventHandle), EvtRenderEventValues, 0, nil, &bufferUsed, &propertyCount) + if bufferUsed == 0 { + return nil, err } - - return int64(field), true -} - -// Get the formatted string that represents this message. This method wraps EvtFormatMessage. -func FormatMessage(eventPublisherHandle PublisherHandle, eventHandle EventHandle, format EVT_FORMAT_MESSAGE_FLAGS) (string, error) { - cString := C.GetFormattedMessage(C.ULONGLONG(eventPublisherHandle), C.ULONGLONG(eventHandle), C.int(format)) - if cString == nil { - return "", GetLastError() + buffer := make([]byte, bufferUsed) + bufSize := bufferUsed + err = EvtRender(syscall.Handle(renderContext), syscall.Handle(eventHandle), EvtRenderEventValues, bufSize, (*uint16)(unsafe.Pointer(&buffer[0])), &bufferUsed, &propertyCount) + if err != nil { + return nil, err } - value := C.GoString(cString) - C.free(unsafe.Pointer(cString)) - return value, nil + return NewEvtVariant(buffer), nil } -// Get the formatted string for the last error which occurred. Wraps GetLastError and FormatMessage. -func GetLastError() error { - errStr := C.GetLastErrorString() - err := errors.New(C.GoString(errStr)) - C.LocalFree(C.HLOCAL(errStr)) - return err -} - -// Render the system properties from the event and returns an array of properties. -// Properties can be accessed using RenderStringField, RenderIntField, RenderFileTimeField, -// or RenderUIntField depending on type. This buffer must be freed after use. -func RenderEventValues(renderContext SysRenderContext, eventHandle EventHandle) (RenderedFields, error) { - values := RenderedFields(C.RenderEventValues(C.ULONGLONG(renderContext), C.ULONGLONG(eventHandle))) - if values == nil { - return nil, GetLastError() +/* Get a handle that represents the publisher of the event, given the rendered event values. */ +func GetEventPublisherHandle(renderedFields EvtVariant) (PublisherHandle, error) { + publisher, err := renderedFields.String(EvtSystemProviderName) + if err != nil { + return 0, err } - return values, nil -} - -// Get a handle that represents the publisher of the event, given the rendered event values. -func GetEventPublisherHandle(renderedFields RenderedFields) (PublisherHandle, error) { - handle := PublisherHandle(C.GetEventPublisherHandle(C.PVOID(renderedFields))) - if handle == 0 { - return 0, GetLastError() + widePublisher, err := syscall.UTF16PtrFromString(publisher) + if err != nil { + return 0, err + } + handle, err := EvtOpenPublisherMetadata(0, widePublisher, nil, 0, 0) + if err != nil { + return 0, err } - return handle, nil + return PublisherHandle(handle), nil } -// Close an event handle. +/* Close an event handle. */ func CloseEventHandle(handle uint64) error { - if C.CloseEvtHandle(C.ULONGLONG(handle)) != 1 { - return GetLastError() - } - return nil + return EvtClose(syscall.Handle(handle)) } -// Cancel pending actions on the event handle. +/* Cancel pending actions on the event handle. */ func CancelEventHandle(handle uint64) error { - if C.CancelEvtHandle(C.ULONGLONG(handle)) != 1 { - return GetLastError() + err := EvtCancel(syscall.Handle(handle)) + if err != nil { + return err } return nil } -func Free(ptr unsafe.Pointer) { - C.free(ptr) -} - /* Get the first event in the log, for testing */ func getTestEventHandle() (EventHandle, error) { - handle := C.GetTestEventHandle() - if handle == 0 { - return 0, GetLastError() + wideQuery, _ := syscall.UTF16PtrFromString("*") + wideChannel, _ := syscall.UTF16PtrFromString("Application") + handle, err := EvtQuery(0, wideChannel, wideQuery, EvtQueryChannelPath) + if err != nil { + return 0, err } - return EventHandle(handle), nil -} - -/* These are entry points for the callback to hand the pointer to Go-land. - Note: handles are only valid within the callback. Don't pass them out. */ - -//export eventCallbackError -func eventCallbackError(handle C.ULONGLONG, logWatcher unsafe.Pointer) { - watcher := (*LogEventCallbackWrapper)(logWatcher).callback - watcher.PublishError(fmt.Errorf("Event log callback got error: %v", GetLastError())) + var record syscall.Handle + var recordsReturned uint32 + err = EvtNext(handle, 1, &record, 500, 0, &recordsReturned) + if err != nil { + EvtClose(handle) + return 0, nil + } + EvtClose(handle) + return EventHandle(record), nil } -//export eventCallback -func eventCallback(handle C.ULONGLONG, logWatcher unsafe.Pointer) { - watcher := (*LogEventCallbackWrapper)(logWatcher).callback - watcher.PublishEvent(EventHandle(handle)) +func eventCallback(Action uint32, Context unsafe.Pointer, handle syscall.Handle) uintptr { + watcher := (*LogEventCallbackWrapper)(Context).callback + if Action == 0 { + watcher.PublishError(fmt.Errorf("Event log callback got error: %v", GetLastError())) + } else { + watcher.PublishEvent(EventHandle(handle)) + } + return 0 } diff --git a/event.h b/event.h deleted file mode 100644 index 2221b5b..0000000 --- a/event.h +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include "winevt.h" -#include -#include -#include - -/* Handles really should be EVT_HANDLE, but Go will sometimes - try to check if pointers are valid, and handles aren't necessarily - pointers (although they have type PVOID). So we pass handles to Go as - 64-bit unsigned ints. */ - -// Create a new listener on the given channel. Events will be passed -// to the callback of *pWatcher. Starts at the current position in the log -ULONGLONG CreateListener(char* channel, char* query, int startpos, PVOID pWatcher); - -// Create a new listener on the given channel. Events will be passed -// to the callback of *pWatcher. Starts at the given bookmark handle. -// Note: This doesn't set the strict flag - if the log was truncated between -// the bookmark and now, it'll continue silently from the earliest event. -ULONGLONG CreateListenerFromBookmark(char* channel, char* query, PVOID pWatcher, ULONGLONG hBookmark); - -// Get the string for the last error code -char* GetLastErrorString(); - -// Render the fields for the given context. Allocates an array -// of values based on the context, these can be accessed using -// GetRenderedValue. Buffer must be freed by the caller. -PVOID RenderEventValues(ULONGLONG hContext, ULONGLONG hEvent); - -// Get the type of the variable at the given index in the array. -// Possible types are EvtVarType* -int GetRenderedValueType(PVOID pRenderedValues, int property); - -// Get the value of the variable at the given index. You must know -// the type or this will go badly. -LONGLONG GetRenderedSByteValue(PVOID pRenderedValues, int property); -LONGLONG GetRenderedInt16Value(PVOID pRenderedValues, int property); -LONGLONG GetRenderedInt32Value(PVOID pRenderedValues, int property); -LONGLONG GetRenderedInt64Value(PVOID pRenderedValues, int property); -ULONGLONG GetRenderedByteValue(PVOID pRenderedValues, int property); -ULONGLONG GetRenderedUInt16Value(PVOID pRenderedValues, int property); -ULONGLONG GetRenderedUInt32Value(PVOID pRenderedValues, int property); -ULONGLONG GetRenderedUInt64Value(PVOID pRenderedValues, int property); -// Returns a pointer to a string that must be freed by the caller -char* GetRenderedStringValue(PVOID pRenderedValues, int property); -// Returns a unix epoch timestamp in milliseconds, not a FileTime -ULONGLONG GetRenderedFileTimeValue(PVOID pRenderedValues, int property); - -// Format the event into a string using details from the event publisher. -// Valid formats are EvtFormatMessage* -char* GetFormattedMessage(ULONGLONG hEventPublisher, ULONGLONG hEvent, int format); - -// Get the handle for the publisher, this must be closed by the caller. -// Needed to format messages since schema is publisher-specific. -ULONGLONG GetEventPublisherHandle(PVOID pRenderedValues); - -// Cast the ULONGLONG back to a pointer and close it -int CloseEvtHandle(ULONGLONG hEvent); - -// Cancel pending operations on a handle -int CancelEvtHandle(ULONGLONG hEvent); - -// Create a context for RenderEventValues that decodes standard system properties. -// Properties in the resulting array can be accessed using the indices from -// EvtSystem* -ULONGLONG CreateSystemRenderContext(); - -// For testing, get a handle on the first event in the log -ULONGLONG GetTestEventHandle(); \ No newline at end of file diff --git a/event_test.go b/event_test.go index b7c077b..51c5365 100644 --- a/event_test.go +++ b/event_test.go @@ -5,7 +5,7 @@ package winlog import ( "encoding/xml" . "testing" - "unsafe" + "time" ) type ProviderXml struct { @@ -19,7 +19,7 @@ type EventIdXml struct { } type TimeCreatedXml struct { - SystemTime string `xml:"SystemTime,attr"` + SystemTime time.Time `xml:"SystemTime,attr"` } type ExecutionXml struct { @@ -77,7 +77,6 @@ func TestXmlRenderMatchesOurs(t *T) { if err != nil { t.Fatal(err) } - defer Free(unsafe.Pointer(renderedFields)) publisherHandle, err := GetEventPublisherHandle(renderedFields) if err != nil { t.Fatal(err) @@ -115,7 +114,7 @@ func TestXmlRenderMatchesOurs(t *T) { assertEqual(event.OpcodeText, eventXml.RenderingInfo.OpcodeText, t) assertEqual(event.ChannelText, eventXml.RenderingInfo.ChannelText, t) assertEqual(event.ProviderText, eventXml.RenderingInfo.ProviderText, t) - assertEqual(event.Created.Format("2006-01-02T15:04:05.000000000Z"), eventXml.System.TimeCreated.SystemTime, t) + assertEqual(event.Created.UTC(), eventXml.System.TimeCreated.SystemTime.UTC(), t) } func BenchmarkXmlDecode(b *B) { @@ -146,7 +145,6 @@ func BenchmarkXmlDecode(b *B) { if err = xml.Unmarshal([]byte(xmlString), &eventXml); err != nil { b.Fatal(err) } - Free(unsafe.Pointer(renderedFields)) CloseEventHandle(uint64(publisherHandle)) } } diff --git a/evt_variant.go b/evt_variant.go new file mode 100644 index 0000000..16347d5 --- /dev/null +++ b/evt_variant.go @@ -0,0 +1,128 @@ +package winlog + +import ( + "fmt" + "syscall" + "time" + "unsafe" +) + +/* Convenience functions to get values out of + an array of EvtVariant structures */ + +const ( + EvtVarTypeNull = iota + EvtVarTypeString + EvtVarTypeAnsiString + EvtVarTypeSByte + EvtVarTypeByte + EvtVarTypeInt16 + EvtVarTypeUInt16 + EvtVarTypeInt32 + EvtVarTypeUInt32 + EvtVarTypeInt64 + EvtVarTypeUInt64 + EvtVarTypeSingle + EvtVarTypeDouble + EvtVarTypeBoolean + EvtVarTypeBinary + EvtVarTypeGuid + EvtVarTypeSizeT + EvtVarTypeFileTime + EvtVarTypeSysTime + EvtVarTypeSid + EvtVarTypeHexInt32 + EvtVarTypeHexInt64 + EvtVarTypeEvtHandle + EvtVarTypeEvtXml +) + +type evtVariant struct { + Data uint64 + Count uint32 + Type uint32 +} + +type fileTime struct { + lowDateTime uint32 + highDateTime uint32 +} + +type EvtVariant []byte + +/* Given a byte array from EvtRender, make an EvtVariant. + EvtVariant wraps an array of variables. */ +func NewEvtVariant(buffer []byte) EvtVariant { + return EvtVariant(buffer) +} + +func (e EvtVariant) elemAt(index uint32) *evtVariant { + return (*evtVariant)(unsafe.Pointer(uintptr(16*index) + uintptr(unsafe.Pointer(&e[0])))) +} + +/* Return the string value of the variable at `index`. If the + variable isn't a string, an error is returned */ +func (e EvtVariant) String(index uint32) (string, error) { + elem := e.elemAt(index) + if elem.Type != EvtVarTypeString { + return "", fmt.Errorf("EvtVariant at index %v was not of type string, type was %v", index, elem.Type) + } + wideString := (*[1 << 30]uint16)(unsafe.Pointer(uintptr(elem.Data))) + str := syscall.UTF16ToString(wideString[0 : elem.Count+1]) + return str, nil +} + +/* Return the unsigned integer value at `index`. If the variable + isn't a Byte, UInt16, UInt32 or UInt64 an error is returned. */ +func (e EvtVariant) Uint(index uint32) (uint64, error) { + elem := e.elemAt(index) + switch elem.Type { + case EvtVarTypeByte: + return uint64(byte(elem.Data)), nil + case EvtVarTypeUInt16: + return uint64(uint16(elem.Data)), nil + case EvtVarTypeUInt32: + return uint64(uint32(elem.Data)), nil + case EvtVarTypeUInt64: + return uint64(elem.Data), nil + default: + return 0, fmt.Errorf("EvtVariant at index %v was not an unsigned integer, type is %v", index, elem.Type) + } +} + +/* Return the integer value at `index`. If the variable + isn't a SByte, Int16, Int32 or Int64 an error is returned. */ +func (e EvtVariant) Int(index uint32) (int64, error) { + elem := e.elemAt(index) + switch elem.Type { + case EvtVarTypeSByte: + return int64(byte(elem.Data)), nil + case EvtVarTypeInt16: + return int64(int16(elem.Data)), nil + case EvtVarTypeInt32: + return int64(int32(elem.Data)), nil + case EvtVarTypeInt64: + return int64(elem.Data), nil + default: + return 0, fmt.Errorf("EvtVariant at index %v was not an integer, type is %v", index, elem.Type) + } +} + +/* Return the FileTime at `index`, converted to Time.time. If the + variable isn't a FileTime an error is returned */ +func (e EvtVariant) FileTime(index uint32) (time.Time, error) { + elem := e.elemAt(index) + if elem.Type != EvtVarTypeFileTime { + return time.Now(), fmt.Errorf("EvtVariant at index %v was not of type FileTime, type was %v", index, elem.Type) + } + var t *fileTime = (*fileTime)(unsafe.Pointer(&elem.Data)) + timeSecs := (((int64(t.highDateTime) << 32) | int64(t.lowDateTime)) / 10000000) - int64(11644473600) + timeNano := (((int64(t.highDateTime) << 32) | int64(t.lowDateTime)) % 10000000) * 100 + return time.Unix(timeSecs, timeNano), nil +} + +/* Return whether the variable was actually set, or whether it + has null type */ +func (e EvtVariant) IsNull(index uint32) bool { + return e.elemAt(index).Type == EvtVarTypeNull +} diff --git a/example/main.go b/example/main.go index 96deca9..cd1e85c 100644 --- a/example/main.go +++ b/example/main.go @@ -11,7 +11,7 @@ func main() { fmt.Printf("Couldn't create watcher: %v\n", err) return } - err = watcher.SubscribeFromBeginning("Application") + err = watcher.SubscribeFromBeginning("Application", "*") if err != nil { fmt.Printf("Couldn't subscribe to Application: %v", err) } @@ -19,7 +19,7 @@ func main() { select { case evt := <-watcher.Event(): fmt.Printf("Event: %v\n", evt) - bookmark := evt.GetBookmark() + bookmark := evt.Bookmark fmt.Printf("Bookmark: %v\n", bookmark) case err := <-watcher.Error(): fmt.Printf("Error: %v\n\n", err) diff --git a/structs.go b/structs.go index b35f832..5575339 100644 --- a/structs.go +++ b/structs.go @@ -3,7 +3,6 @@ package winlog import ( "sync" "time" - "unsafe" ) // Stores the common fields from a log event @@ -61,7 +60,6 @@ type SysRenderContext uint64 type ListenerHandle uint64 type PublisherHandle uint64 type EventHandle uint64 -type RenderedFields unsafe.Pointer type BookmarkHandle uint64 type LogEventCallback interface { diff --git a/winevt.go b/winevt.go new file mode 100644 index 0000000..7f04fd3 --- /dev/null +++ b/winevt.go @@ -0,0 +1,201 @@ +// +build windows + +package winlog + +import ( + "syscall" + "unsafe" +) + +var ( + winevtDll *syscall.DLL + evtCreateBookmark *syscall.Proc + evtUpdateBookmark *syscall.Proc + evtRender *syscall.Proc + evtClose *syscall.Proc + evtCancel *syscall.Proc + evtFormatMessage *syscall.Proc + evtCreateRenderContext *syscall.Proc + evtSubscribe *syscall.Proc + evtQuery *syscall.Proc + evtOpenPublisherMetadata *syscall.Proc + evtNext *syscall.Proc +) + +func init() { + winevtDll = syscall.MustLoadDLL("wevtapi.dll") + evtCreateBookmark = winevtDll.MustFindProc("EvtCreateBookmark") + evtUpdateBookmark = winevtDll.MustFindProc("EvtUpdateBookmark") + evtRender = winevtDll.MustFindProc("EvtRender") + evtClose = winevtDll.MustFindProc("EvtClose") + evtCancel = winevtDll.MustFindProc("EvtCancel") + evtFormatMessage = winevtDll.MustFindProc("EvtFormatMessage") + evtCreateRenderContext = winevtDll.MustFindProc("EvtCreateRenderContext") + evtSubscribe = winevtDll.MustFindProc("EvtSubscribe") + evtQuery = winevtDll.MustFindProc("EvtQuery") + evtOpenPublisherMetadata = winevtDll.MustFindProc("EvtOpenPublisherMetadata") + evtNext = winevtDll.MustFindProc("EvtNext") +} + +type EVT_SUBSCRIBE_FLAGS int + +const ( + _ = iota + EvtSubscribeToFutureEvents + EvtSubscribeStartAtOldestRecord + EvtSubscribeStartAfterBookmark +) + +/* Fields that can be rendered with GetRendered*Value */ +type EVT_SYSTEM_PROPERTY_ID int + +const ( + EvtSystemProviderName = iota + EvtSystemProviderGuid + EvtSystemEventID + EvtSystemQualifiers + EvtSystemLevel + EvtSystemTask + EvtSystemOpcode + EvtSystemKeywords + EvtSystemTimeCreated + EvtSystemEventRecordId + EvtSystemActivityID + EvtSystemRelatedActivityID + EvtSystemProcessID + EvtSystemThreadID + EvtSystemChannel + EvtSystemComputer + EvtSystemUserID + EvtSystemVersion +) + +/* Formatting modes for GetFormattedMessage */ +type EVT_FORMAT_MESSAGE_FLAGS int + +const ( + _ = iota + EvtFormatMessageEvent + EvtFormatMessageLevel + EvtFormatMessageTask + EvtFormatMessageOpcode + EvtFormatMessageKeyword + EvtFormatMessageChannel + EvtFormatMessageProvider + EvtFormatMessageId + EvtFormatMessageXml +) + +type EVT_RENDER_FLAGS uint32 + +const ( + EvtRenderEventValues = iota + EvtRenderEventXml + EvtRenderBookmark +) + +type EVT_RENDER_CONTEXT_FLAGS uint32 + +const ( + EvtRenderContextValues = iota + EvtRenderContextSystem + EvtRenderContextUser +) + +type EVT_QUERY_FLAGS uint32 + +const ( + EvtQueryChannelPath = 0x1 + EvtQueryFilePath = 0x2 + EvtQueryForwardDirection = 0x100 + EvtQueryReverseDirection = 0x200 + EvtQueryTolerateQueryErrors = 0x1000 +) + +func EvtCreateBookmark(BookmarkXml *uint16) (syscall.Handle, error) { + r1, _, err := evtCreateBookmark.Call(uintptr(unsafe.Pointer(BookmarkXml))) + if r1 == 0 { + return 0, err + } + return syscall.Handle(r1), nil +} + +func EvtUpdateBookmark(Bookmark, Event syscall.Handle) error { + r1, _, err := evtUpdateBookmark.Call(uintptr(Bookmark), uintptr(Event)) + if r1 == 0 { + return err + } + return nil +} + +func EvtRender(Context, Fragment syscall.Handle, Flags, BufferSize uint32, Buffer *uint16, BufferUsed, PropertyCount *uint32) error { + r1, _, err := evtRender.Call(uintptr(Context), uintptr(Fragment), uintptr(Flags), uintptr(BufferSize), uintptr(unsafe.Pointer(Buffer)), uintptr(unsafe.Pointer(BufferUsed)), uintptr(unsafe.Pointer(PropertyCount))) + if r1 == 0 { + return err + } + return nil +} + +func EvtClose(Object syscall.Handle) error { + r1, _, err := evtClose.Call(uintptr(Object)) + if r1 == 0 { + return err + } + return nil +} + +func EvtFormatMessage(PublisherMetadata, Event syscall.Handle, MessageId, ValueCount uint32, Values *byte, Flags, BufferSize uint32, Buffer *uint16, BufferUsed *uint32) error { + r1, _, err := evtFormatMessage.Call(uintptr(PublisherMetadata), uintptr(Event), uintptr(MessageId), uintptr(ValueCount), uintptr(unsafe.Pointer(Values)), uintptr(Flags), uintptr(BufferSize), uintptr(unsafe.Pointer(Buffer)), uintptr(unsafe.Pointer(BufferUsed))) + if r1 == 0 { + return err + } + return nil +} + +func EvtCreateRenderContext(ValuePathsCount uint32, ValuePaths uintptr, Flags uint32) (syscall.Handle, error) { + r1, _, err := evtCreateRenderContext.Call(uintptr(ValuePathsCount), ValuePaths, uintptr(Flags)) + if r1 == 0 { + return 0, err + } + return syscall.Handle(r1), nil +} + +func EvtSubscribe(Session, SignalEvent syscall.Handle, ChannelPath, Query *uint16, Bookmark syscall.Handle, context uintptr, Callback uintptr, Flags uint32) (syscall.Handle, error) { + r1, _, err := evtSubscribe.Call(uintptr(Session), uintptr(SignalEvent), uintptr(unsafe.Pointer(ChannelPath)), uintptr(unsafe.Pointer(Query)), uintptr(Bookmark), context, Callback, uintptr(Flags)) + if r1 == 0 { + return 0, err + } + return syscall.Handle(r1), nil +} + +func EvtQuery(Session syscall.Handle, Path, Query *uint16, Flags uint32) (syscall.Handle, error) { + r1, _, err := evtQuery.Call(uintptr(Session), uintptr(unsafe.Pointer(Path)), uintptr(unsafe.Pointer(Query)), uintptr(Flags)) + if r1 == 0 { + return 0, err + } + return syscall.Handle(r1), nil +} + +func EvtOpenPublisherMetadata(Session syscall.Handle, PublisherIdentity, LogFilePath *uint16, Locale, Flags uint32) (syscall.Handle, error) { + r1, _, err := evtOpenPublisherMetadata.Call(uintptr(Session), uintptr(unsafe.Pointer(PublisherIdentity)), uintptr(unsafe.Pointer(LogFilePath)), uintptr(Locale), uintptr(Flags)) + if r1 == 0 { + return 0, err + } + return syscall.Handle(r1), nil +} + +func EvtCancel(handle syscall.Handle) error { + r1, _, err := evtCancel.Call(uintptr(handle)) + if r1 == 0 { + return err + } + return nil +} + +func EvtNext(ResultSet syscall.Handle, EventArraySize uint32, EventArray *syscall.Handle, Timeout, Flags uint32, Returned *uint32) error { + r1, _, err := evtNext.Call(uintptr(ResultSet), uintptr(EventArraySize), uintptr(unsafe.Pointer(EventArray)), uintptr(Timeout), uintptr(Flags), uintptr(unsafe.Pointer(Returned))) + if r1 == 0 { + return err + } + return nil +} diff --git a/winlog.go b/winlog.go index 5d9539d..bb6ba23 100644 --- a/winlog.go +++ b/winlog.go @@ -4,7 +4,6 @@ package winlog import ( "fmt" - "unsafe" ) func (self *WinLogWatcher) Event() <-chan *WinLogEvent { @@ -69,8 +68,8 @@ func (self *WinLogWatcher) subscribeWithoutBookmark(channel, query string, flags } // Subscribe to a Windows Event Log channel, starting with the first event in the log -// after the bookmarked event. There may be a gap if events have been purged. `query` -// is an XPath expression for filtering events: to recieve all events on the channel, +// after the bookmarked event. There may be a gap if events have been purged. `query` +// is an XPath expression for filtering events: to recieve all events on the channel, // use "*" as the query func (self *WinLogWatcher) SubscribeFromBookmark(channel, query string, xmlString string) error { self.watchMutex.Lock() @@ -139,19 +138,19 @@ func (self *WinLogWatcher) convertEvent(handle EventHandle) (*WinLogEvent, error } /* If fields don't exist we include the nil value */ - computerName, _ := RenderStringField(renderedFields, EvtSystemComputer) - providerName, _ := RenderStringField(renderedFields, EvtSystemProviderName) - channel, _ := RenderStringField(renderedFields, EvtSystemChannel) - level, _ := RenderUIntField(renderedFields, EvtSystemLevel) - task, _ := RenderUIntField(renderedFields, EvtSystemTask) - opcode, _ := RenderUIntField(renderedFields, EvtSystemOpcode) - recordId, _ := RenderUIntField(renderedFields, EvtSystemEventRecordId) - qualifiers, _ := RenderUIntField(renderedFields, EvtSystemQualifiers) - eventId, _ := RenderUIntField(renderedFields, EvtSystemEventID) - processId, _ := RenderUIntField(renderedFields, EvtSystemProcessID) - threadId, _ := RenderUIntField(renderedFields, EvtSystemThreadID) - version, _ := RenderUIntField(renderedFields, EvtSystemVersion) - created, _ := RenderFileTimeField(renderedFields, EvtSystemTimeCreated) + computerName, _ := renderedFields.String(EvtSystemComputer) + providerName, _ := renderedFields.String(EvtSystemProviderName) + channel, _ := renderedFields.String(EvtSystemChannel) + level, _ := renderedFields.Uint(EvtSystemLevel) + task, _ := renderedFields.Uint(EvtSystemTask) + opcode, _ := renderedFields.Uint(EvtSystemOpcode) + recordId, _ := renderedFields.Uint(EvtSystemEventRecordId) + qualifiers, _ := renderedFields.Uint(EvtSystemQualifiers) + eventId, _ := renderedFields.Uint(EvtSystemEventID) + processId, _ := renderedFields.Uint(EvtSystemProcessID) + threadId, _ := renderedFields.Uint(EvtSystemThreadID) + version, _ := renderedFields.Uint(EvtSystemVersion) + created, _ := renderedFields.FileTime(EvtSystemTimeCreated) msgText, _ := FormatMessage(publisherHandle, handle, EvtFormatMessageEvent) lvlText, _ := FormatMessage(publisherHandle, handle, EvtFormatMessageLevel) @@ -162,7 +161,6 @@ func (self *WinLogWatcher) convertEvent(handle EventHandle) (*WinLogEvent, error idText, _ := FormatMessage(publisherHandle, handle, EvtFormatMessageId) CloseEventHandle(uint64(publisherHandle)) - Free(unsafe.Pointer(renderedFields)) event := WinLogEvent{ ProviderName: providerName,