訂閱事件
若要訂閱事件,請呼叫 EvtSubscribe 函式 。 您可以從一或多個管理員或操作通道訂閱事件。 通道可以存在於本機電腦或遠端電腦上。 若要指定您想要訂閱的事件,您可以使用 XPath 查詢或結構 XML 查詢。 如需撰寫查詢的詳細資訊,請參閱 取用事件。
Windows 事件記錄檔針對事件訂用帳戶提供兩個模型:
- 推送模型會以非同步方式將事件推送至您實作的回呼。
- 提取模型會使用您建立的事件控制碼,以在有符合查詢準則的事件可用時發出訊號。 然後,您可以列舉結果集中的事件。
您可以訂閱過去和未來的事件、僅限未來事件,或從書簽事件之後開始的過去和未來事件。
如需轉譯事件的詳細資訊,請參閱 轉譯事件。
如果您想要訂閱離開位置的事件,請在結束訂用帳戶之前建立書簽,並在下次訂閱事件時使用它。 如需詳細資訊,請參閱 書簽事件。
發送訂閱
下列範例示範如何訂閱事件、實作訂用帳戶回呼,以及轉譯事件。
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <winevt.h>
#pragma comment(lib, "wevtapi.lib")
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent);
DWORD PrintEvent(EVT_HANDLE hEvent);
void main(void)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hSubscription = NULL;
LPWSTR pwsPath = L"<channel name goes here>";
LPWSTR pwsQuery = L"<xpath query goes here>";
// Subscribe to events beginning with the oldest event in the channel. The subscription
// will return all current events in the channel and any future events that are raised
// while the application is active.
hSubscription = EvtSubscribe(NULL, NULL, pwsPath, pwsQuery, NULL, NULL,
(EVT_SUBSCRIBE_CALLBACK)SubscriptionCallback, EvtSubscribeStartAtOldestRecord);
if (NULL == hSubscription)
{
status = GetLastError();
if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
wprintf(L"Channel %s was not found.\n", pwsPath);
else if (ERROR_EVT_INVALID_QUERY == status)
// You can call EvtGetExtendedStatus to get information as to why the query is not valid.
wprintf(L"The query \"%s\" is not valid.\n", pwsQuery);
else
wprintf(L"EvtSubscribe failed with %lu.\n", status);
goto cleanup;
}
wprintf(L"Hit any key to quit\n\n");
while (!_kbhit())
Sleep(10);
cleanup:
if (hSubscription)
EvtClose(hSubscription);
}
// The callback that receives the events that match the query criteria.
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent)
{
UNREFERENCED_PARAMETER(pContext);
DWORD status = ERROR_SUCCESS;
switch(action)
{
// You should only get the EvtSubscribeActionError action if your subscription flags
// includes EvtSubscribeStrict and the channel contains missing event records.
case EvtSubscribeActionError:
if (ERROR_EVT_QUERY_RESULT_STALE == (DWORD)hEvent)
{
wprintf(L"The subscription callback was notified that event records are missing.\n");
// Handle if this is an issue for your application.
}
else
{
wprintf(L"The subscription callback received the following Win32 error: %lu\n", (DWORD)hEvent);
}
break;
case EvtSubscribeActionDeliver:
if (ERROR_SUCCESS != (status = PrintEvent(hEvent)))
{
goto cleanup;
}
break;
default:
wprintf(L"SubscriptionCallback: Unknown action.\n");
}
cleanup:
if (ERROR_SUCCESS != status)
{
// End subscription - Use some kind of IPC mechanism to signal
// your application to close the subscription handle.
}
return status; // The service ignores the returned status.
}
// Render the event as an XML string and print it.
DWORD PrintEvent(EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pRenderedContent = NULL;
if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pRenderedContent = (LPWSTR)malloc(dwBufferSize);
if (pRenderedContent)
{
EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
}
else
{
wprintf(L"malloc failed\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
wprintf(L"EvtRender failed with %d\n", status);
goto cleanup;
}
}
wprintf(L"%s\n\n", pRenderedContent);
cleanup:
if (pRenderedContent)
free(pRenderedContent);
return status;
}
提取訂閱
提取訂閱模型會使用事件物件 (查看 CreateEventEx 函式) ,以向應用程式發出符合查詢準則之結果集中的事件。 建立迴圈建構,等候事件物件直到發出訊號為止。 然後,在迴圈中呼叫 EvtNext 函式,以列舉結果集中的事件。 當 EvtNext 函式失敗,並將最後一個錯誤設定為ERROR_NO_MORE_ITEMS時,請重設事件物件,並等候服務在結果集中發生事件時再次向物件發出訊號。
下列範例示範如何使用提取訂閱模型。
#include <windows.h>
#include <conio.h>
#include <stdio.h>
#include <winevt.h>
#pragma comment(lib, "wevtapi.lib")
#define ARRAY_SIZE 10
DWORD EnumerateResults(EVT_HANDLE hResults);
DWORD PrintEvent(EVT_HANDLE hEvent);
BOOL IsKeyEvent(HANDLE hStdIn);
void __cdecl wmain()
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hSubscription = NULL;
LPWSTR pwsPath = L"<channel name goes here>";
LPWSTR pwsQuery = L"*";
HANDLE aWaitHandles[2];
DWORD dwWait = 0;
// Get a handle for console input, so you can break out of the loop.
aWaitHandles[0] = GetStdHandle(STD_INPUT_HANDLE);
if (INVALID_HANDLE_VALUE == aWaitHandles[0])
{
wprintf(L"GetStdHandle failed with %lu.\n", GetLastError());
goto cleanup;
}
// Get a handle to a manual reset event object that the subscription will signal
// when events become available that match your query criteria.
aWaitHandles[1] = CreateEvent(NULL, TRUE, TRUE, NULL);
if (NULL == aWaitHandles[1])
{
wprintf(L"CreateEvent failed with %lu.\n", GetLastError());
goto cleanup;
}
// Subscribe to events.
hSubscription = EvtSubscribe(NULL, aWaitHandles[1], pwsPath, pwsQuery, NULL, NULL, NULL, EvtSubscribeStartAtOldestRecord);
if (NULL == hSubscription)
{
status = GetLastError();
if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
wprintf(L"Channel %s was not found.\n", pwsPath);
else if (ERROR_EVT_INVALID_QUERY == status)
wprintf(L"The query %s was not found.\n", pwsQuery);
else
wprintf(L"EvtSubscribe failed with %lu.\n", status);
goto cleanup;
}
wprintf(L"Press any key to quit.\n");
// Loop until the user presses a key or there is an error.
while (true)
{
dwWait = WaitForMultipleObjects(sizeof(aWaitHandles)/sizeof(HANDLE), aWaitHandles, FALSE, INFINITE);
if (0 == dwWait - WAIT_OBJECT_0) // Console input
{
if (IsKeyEvent(aWaitHandles[0]))
break;
}
else if (1 == dwWait - WAIT_OBJECT_0) // Query results
{
if (ERROR_NO_MORE_ITEMS != (status = EnumerateResults(hSubscription)))
{
break;
}
ResetEvent(aWaitHandles[1]);
}
else
{
if (WAIT_FAILED == dwWait)
{
wprintf(L"WaitForSingleObject failed with %lu\n", GetLastError());
}
break;
}
}
cleanup:
if (hSubscription)
EvtClose(hSubscription);
if (aWaitHandles[0])
CloseHandle(aWaitHandles[0]);
if (aWaitHandles[1])
CloseHandle(aWaitHandles[1]);
}
// Enumerate the events in the result set.
DWORD EnumerateResults(EVT_HANDLE hResults)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hEvents[ARRAY_SIZE];
DWORD dwReturned = 0;
while (true)
{
// Get a block of events from the result set.
if (!EvtNext(hResults, ARRAY_SIZE, hEvents, INFINITE, 0, &dwReturned))
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
wprintf(L"EvtNext failed with %lu\n", status);
}
goto cleanup;
}
// For each event, call the PrintEvent function which renders the
// event for display.
for (DWORD i = 0; i < dwReturned; i++)
{
if (ERROR_SUCCESS == (status = PrintEvent(hEvents[i])))
{
EvtClose(hEvents[i]);
hEvents[i] = NULL;
}
else
{
goto cleanup;
}
}
}
cleanup:
// Closes any events in case an error occurred above.
for (DWORD i = 0; i < dwReturned; i++)
{
if (NULL != hEvents[i])
EvtClose(hEvents[i]);
}
return status;
}
// Render the event as an XML string and print it.
DWORD PrintEvent(EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pRenderedContent = NULL;
// The EvtRenderEventXml flag tells EvtRender to render the event as an XML string.
if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pRenderedContent = (LPWSTR)malloc(dwBufferSize);
if (pRenderedContent)
{
EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
}
else
{
wprintf(L"malloc failed\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
wprintf(L"EvtRender failed with %d\n", GetLastError());
goto cleanup;
}
}
wprintf(L"\n\n%s", pRenderedContent);
cleanup:
if (pRenderedContent)
free(pRenderedContent);
return status;
}
// Determines whether the console input was a key event.
BOOL IsKeyEvent(HANDLE hStdIn)
{
INPUT_RECORD Record[128];
DWORD dwRecordsRead = 0;
BOOL fKeyPress = FALSE;
if (ReadConsoleInput(hStdIn, Record, 128, &dwRecordsRead))
{
for (DWORD i = 0; i < dwRecordsRead; i++)
{
if (KEY_EVENT == Record[i].EventType)
{
fKeyPress = TRUE;
break;
}
}
}
return fKeyPress;
}