538 lines
13 KiB
C++
538 lines
13 KiB
C++
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
/*++
|
|
|
|
Module Name:
|
|
|
|
PullSubscription.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements a sample demonstrating how to use pull mode
|
|
Event Subscription.
|
|
|
|
Environment:
|
|
|
|
Windows vista or above.
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <winevt.h>
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
|
|
//
|
|
// N.B.
|
|
// Channel - The subsciption channel name.
|
|
// Query - An XPath event query string.
|
|
// BookMarkXml - A BookMark string which will be used to subscribe after a bookmark.
|
|
// SignalEvent - A NT event handle used to syncronize the work thread and main thread.
|
|
// Stop - Indicating if the subscription needs to pause.
|
|
// ErrorCode - The error code associated with the subscription operation.
|
|
//
|
|
|
|
typedef struct _SUBSCRIBE_PARAMETER {
|
|
PCWSTR Channel;
|
|
PCWSTR Query;
|
|
PCWSTR BookMarkXml;
|
|
HANDLE SignalEvent;
|
|
BOOL Stop;
|
|
DWORD ErrorCode;
|
|
} SUBSCRIBE_PARAMETER, *PSUBSCRIBE_PARAMETER;
|
|
|
|
VOID
|
|
DisplayHelp (
|
|
__in PCWSTR ExeName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function displays the usage of the sample tool.
|
|
|
|
Parameters:
|
|
|
|
ExeName - Supplies the sample tool executable name.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
wprintf(L"%s <ChannelName> [XPathQueryString]\n", ExeName);
|
|
wprintf(L"Example: %s Application *[System[(Level=2)]]\n", ExeName);
|
|
wprintf(L"\tAbove example shows to subscribe to all the error events in Application channel.\n");
|
|
}
|
|
|
|
DWORD
|
|
RenderEvent (
|
|
__in EVT_HANDLE Fragment,
|
|
__in EVT_RENDER_FLAGS Flags,
|
|
__out PCWSTR* RenderedXML
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function renders an event to text or renders a bookmark
|
|
handle to XML text.
|
|
|
|
Parameters:
|
|
|
|
Fragment - Supplies the event handle or the bookmark handle.
|
|
|
|
Flags - Supplies the flag for rendering function.
|
|
|
|
RenderedXML - Retrieves the rendered XML text string.
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS means operation succeeds. Other Win32 error
|
|
code indicating the detail error.
|
|
|
|
--*/
|
|
|
|
{
|
|
const DWORD DefaultSize = 2048;
|
|
|
|
DWORD SizeNeeded;
|
|
PVOID Buffer;
|
|
PVOID NewBuffer;
|
|
|
|
//
|
|
// Allocate the memory based on the default size first.
|
|
//
|
|
|
|
Buffer = HeapAlloc(GetProcessHeap(), 0, DefaultSize);
|
|
if (Buffer == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Return to the caller if the default size is enough to hold
|
|
// the final result.
|
|
//
|
|
|
|
if (EvtRender(NULL,
|
|
Fragment,
|
|
Flags,
|
|
DefaultSize,
|
|
Buffer,
|
|
&SizeNeeded,
|
|
NULL)) {
|
|
*RenderedXML = (PCWSTR) Buffer;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If EvtRender fails with unknown error other than
|
|
// ERROR_INSUFFICIENT_BUFFER, return the error directly.
|
|
//
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Reallocate the memory according to the real needed size and
|
|
// call EvtRender again.
|
|
//
|
|
|
|
NewBuffer = HeapReAlloc(GetProcessHeap(), 0, Buffer, SizeNeeded);
|
|
if (NewBuffer == NULL) {
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
Buffer = NewBuffer;
|
|
|
|
if (!EvtRender(NULL,
|
|
Fragment,
|
|
Flags,
|
|
SizeNeeded,
|
|
Buffer,
|
|
&SizeNeeded,
|
|
NULL)) {
|
|
HeapFree(GetProcessHeap(), 0, Buffer);
|
|
return GetLastError();
|
|
}
|
|
|
|
*RenderedXML = (PCWSTR) Buffer;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PrintEventXML (
|
|
__in EVT_HANDLE Event
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function prints the event XML string to the console.
|
|
|
|
Parameters:
|
|
|
|
Event - Supplies the event handle.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCWSTR RenderedXML;
|
|
DWORD Error;
|
|
|
|
Error = RenderEvent(Event, EvtRenderEventXml, &RenderedXML);
|
|
if (Error == ERROR_SUCCESS) {
|
|
wprintf(L"\n%s\n", RenderedXML);
|
|
HeapFree(GetProcessHeap(), 0, (PVOID) RenderedXML);
|
|
} else {
|
|
wprintf(L"\nRenderEventFail with error code=%u\n", Error);
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
SaveBookmarkXML (
|
|
__in EVT_HANDLE BookMark,
|
|
__out PCWSTR* BookMarkXML
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function saves the bookmark text from the bookmark handle.
|
|
|
|
Parameters:
|
|
|
|
BookMark - Supplies the bookmark handle.
|
|
|
|
BookMarkXML - Retrieves the bookmark XML text.
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS means operation succeeds. Other Win32 error
|
|
code indicating the detail error.
|
|
|
|
--*/
|
|
|
|
{
|
|
return RenderEvent(BookMark, EvtRenderBookmark, BookMarkXML);
|
|
}
|
|
|
|
|
|
VOID CALLBACK
|
|
PullSubscription (
|
|
__inout PTP_CALLBACK_INSTANCE Instance,
|
|
__inout_opt PVOID Context,
|
|
__inout PTP_WORK Work
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function implements the main logic of a pull subscription
|
|
of a channel in event log.
|
|
|
|
Parameters:
|
|
|
|
Instance - Supplies a TP_CALLBACK_INSTANCE structure that defines
|
|
the callback instance.
|
|
|
|
Context - Supplies the application-defined data.
|
|
|
|
Work - Supplies a TP_WORK structure that defines the work object that
|
|
generated the callback.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSUBSCRIBE_PARAMETER Parameter;
|
|
|
|
EVT_HANDLE Subscription = NULL;
|
|
EVT_HANDLE BookMark = NULL;
|
|
|
|
const DWORD BatchSize = 16;
|
|
EVT_HANDLE Events[BatchSize];
|
|
DWORD ReturnedNumber;
|
|
|
|
UNREFERENCED_PARAMETER(Instance);
|
|
UNREFERENCED_PARAMETER(Work);
|
|
|
|
Parameter = (PSUBSCRIBE_PARAMETER) Context;
|
|
Parameter->ErrorCode = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Create the bookmark handle.
|
|
//
|
|
|
|
BookMark = EvtCreateBookmark(Parameter->BookMarkXml);
|
|
if (BookMark == NULL) {
|
|
Parameter->ErrorCode = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Create a pull subscription.
|
|
//
|
|
// N.B. When the callback parameter is NULL, it is a pull subscription.
|
|
// When the bookmark passed in is the one saved last time, the
|
|
// subscription will be able to continue from where it stops last
|
|
// time.
|
|
//
|
|
|
|
Subscription = EvtSubscribe(NULL,
|
|
Parameter->SignalEvent,
|
|
Parameter->Channel,
|
|
Parameter->Query,
|
|
BookMark,
|
|
NULL,
|
|
NULL,
|
|
EvtSubscribeStartAfterBookmark);
|
|
|
|
if (Subscription == NULL) {
|
|
Parameter->ErrorCode = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Keep the subscription working if the Stop flag is not set.
|
|
//
|
|
|
|
while (!Parameter->Stop) {
|
|
|
|
//
|
|
// As long as EvtNext can return events, keep consuming them.
|
|
//
|
|
|
|
while (EvtNext(Subscription,
|
|
BatchSize,
|
|
Events,
|
|
INFINITE,
|
|
0,
|
|
&ReturnedNumber)) {
|
|
for (DWORD i = 0; i < ReturnedNumber; i++) {
|
|
PrintEventXML(Events[i]);
|
|
if (BookMark != NULL) {
|
|
EvtUpdateBookmark(BookMark, Events[i]);
|
|
}
|
|
EvtClose(Events[i]);
|
|
}
|
|
|
|
//
|
|
// User needs to stop the subscription, then exit.
|
|
//
|
|
|
|
if (Parameter->Stop) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Subscription meets some un expected error, walk away.
|
|
//
|
|
|
|
if (GetLastError() != ERROR_NO_MORE_ITEMS) {
|
|
Parameter->ErrorCode = GetLastError();
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If no more events coming, wait on the SingalEvent to either get the
|
|
// next event delivery notification or be singnaled by the user to stop
|
|
// the subscription.
|
|
//
|
|
|
|
if (WaitForSingleObject(Parameter->SignalEvent, INFINITE) != WAIT_OBJECT_0) {
|
|
Parameter->ErrorCode = GetLastError();
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
if (Subscription != NULL) {
|
|
EvtClose(Subscription);
|
|
}
|
|
if (BookMark != NULL) {
|
|
|
|
//
|
|
// Save the bookmark XML into the parameter so that next time
|
|
// a subscription can continue from where it stops.
|
|
//
|
|
// N.B. Free the existing bookmark string first before we update
|
|
// bookmark to the new one to prevent memory leaks.
|
|
//
|
|
|
|
if (Parameter->BookMarkXml != NULL) {
|
|
HeapFree(GetProcessHeap(), 0, (PVOID) Parameter->BookMarkXml);
|
|
Parameter->BookMarkXml = NULL;
|
|
}
|
|
DWORD Error = SaveBookmarkXML(BookMark, &Parameter->BookMarkXml);
|
|
|
|
//
|
|
// Below if is used to prevent potential overwrite the existing error
|
|
// code from previous operations.
|
|
//
|
|
|
|
if (Parameter->ErrorCode == ERROR_SUCCESS) {
|
|
Parameter->ErrorCode = Error;
|
|
}
|
|
EvtClose(BookMark);
|
|
}
|
|
}
|
|
|
|
DWORD __cdecl
|
|
wmain (
|
|
__in int argc,
|
|
__in_ecount(argc) PWSTR argv[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The main entry function of the sample.
|
|
|
|
Parameters:
|
|
|
|
argc - Supplies the number of the command arguments.
|
|
|
|
argv - Supplies the real arguments strings.
|
|
|
|
Return Value:
|
|
|
|
The function returns ERROR_SUCCESS means success. It returns other
|
|
windows defined error code indicating the detail error.
|
|
|
|
--*/
|
|
|
|
{
|
|
SUBSCRIBE_PARAMETER SubcriptionParameter;
|
|
PTP_WORK Work;
|
|
WCHAR ControlCharacter;
|
|
BOOL Done = FALSE;
|
|
|
|
if (argc < 2 ||
|
|
(wcscmp(argv[1], L"-h") == 0) ||
|
|
(wcscmp(argv[1], L"-?") == 0) ||
|
|
(wcscmp(argv[1], L"/h") == 0) ||
|
|
(wcscmp(argv[1], L"/?") == 0)) {
|
|
|
|
DisplayHelp(argv[0]);
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set the parameters for the subscription.
|
|
//
|
|
|
|
ZeroMemory(&SubcriptionParameter, sizeof(SubcriptionParameter));
|
|
SubcriptionParameter.Channel = argv[1];
|
|
if (argc >= 3) {
|
|
SubcriptionParameter.Query = argv[2];
|
|
}
|
|
|
|
//
|
|
// Create a thread pool work item to do the subsciption work,
|
|
// while the main thread can control the progress of the work.
|
|
//
|
|
|
|
Work = CreateThreadpoolWork(PullSubscription,
|
|
&SubcriptionParameter,
|
|
NULL);
|
|
if (Work == NULL) {
|
|
wprintf(L"error=%u\n", GetLastError());
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// Create a NT signal event which will be used in subscription.
|
|
//
|
|
|
|
SubcriptionParameter.SignalEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (SubcriptionParameter.SignalEvent == NULL) {
|
|
CloseThreadpoolWork(Work);
|
|
return GetLastError();
|
|
}
|
|
|
|
SubmitThreadpoolWork(Work);
|
|
|
|
while (!Done) {
|
|
|
|
//
|
|
// Wait for user's input.
|
|
//
|
|
|
|
ControlCharacter = _getwch();
|
|
|
|
//
|
|
// If user puts s, it means he wants the subscribption to stop.
|
|
//
|
|
|
|
if (ControlCharacter == L's' || ControlCharacter == 'S') {
|
|
SubcriptionParameter.Stop = TRUE;
|
|
|
|
//
|
|
// Wait for the work to finish himself and then we can
|
|
// collect the error code.
|
|
//
|
|
|
|
SetEvent(SubcriptionParameter.SignalEvent);
|
|
WaitForThreadpoolWorkCallbacks(Work, TRUE);
|
|
|
|
wprintf(L"Subscription stops with last result code: %u\n", SubcriptionParameter.ErrorCode);
|
|
wprintf(L"Press any key to resume the subsciption...\n");
|
|
_getwch();
|
|
|
|
ResetEvent(SubcriptionParameter.SignalEvent);
|
|
SubcriptionParameter.Stop = FALSE;
|
|
|
|
SubmitThreadpoolWork(Work);
|
|
wprintf(L"Subscription has been resumned\n");
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If user puts q, it menas he wants to end the whole process.
|
|
//
|
|
|
|
if (ControlCharacter == L'q' || ControlCharacter == L'Q') {
|
|
Done = TRUE;
|
|
SubcriptionParameter.Stop = TRUE;
|
|
|
|
SetEvent(SubcriptionParameter.SignalEvent);
|
|
WaitForThreadpoolWorkCallbacks(Work, TRUE);
|
|
|
|
wprintf(L"Subsciption finishes with exit code: %u\n",
|
|
SubcriptionParameter.ErrorCode);
|
|
}
|
|
}
|
|
|
|
if (SubcriptionParameter.BookMarkXml != NULL) {
|
|
HeapFree(GetProcessHeap(), 0, (PVOID) SubcriptionParameter.BookMarkXml);
|
|
}
|
|
CloseHandle(SubcriptionParameter.SignalEvent);
|
|
CloseThreadpoolWork(Work);
|
|
return SubcriptionParameter.ErrorCode;
|
|
}
|