책갈피 이벤트
책갈피는 채널 또는 로그 파일에서 이벤트를 식별합니다. 이벤트를 쿼리하거나 구독할 때 책갈피를 사용하여 해당 책갈피가 지정된 이벤트에서 이벤트 읽기를 시작할 수 있습니다. 일반적으로 결과 집합에서 마지막 이벤트의 책갈피를 만듭니다(결과 집합의 모든 이벤트를 열거한 것으로 가정).
다음 절차에서는 이벤트에서 책갈피를 만드는 방법을 설명합니다.
이벤트에서 책갈피를 만들려면
- EvtCreateBookmark 함수를 호출하여 책갈피를 만듭니다. 인수에 대해 NULL 을 전달합니다.
- EvtUpdateBookmark 함수를 호출하여 책갈피를 이벤트로 업데이트합니다. 인수로 이벤트에 핸들을 전달합니다.
- EvtRender 함수를 호출하여 책갈피를 나타내는 XML 문자열을 만듭니다. EvtRenderBookmark를 렌더링 플래그로 전달합니다.
- 나중에 사용할 XML 문자열을 유지합니다(예: 파일 또는 레지스트리에 XML 문자열을 유지할 수 있습니다).
다음 절차에서는 이전 절차에서 유지된 XML 책갈피 문자열을 사용하여 책갈피를 만드는 방법을 설명합니다.
XML 책갈피 문자열을 사용하여 책갈피를 만들려면
- 이전에 유지한 책갈피를 나타내는 XML 문자열을 가져옵니다.
- EvtCreateBookmark 함수를 호출하여 책갈피를 만듭니다. 인수에 대한 XML 문자열을 전달합니다.
다음 절차에서는 쿼리에서 책갈피를 사용하는 방법을 설명합니다.
쿼리에서 책갈피를 사용하려면
- EvtQuery 함수를 호출하여 쿼리와 일치하는 이벤트를 가져옵니다.
- EvtSeek 함수를 호출하여 책갈피가 지정된 이벤트를 찾습니다. 책갈피 및 EvtSeekRelativeToBookmark 플래그에 핸들을 전달합니다.
- 루프에서 EvtNext 함수를 호출하여 책갈피가 지정된 이벤트 이후에 시작되는 이벤트를 열거합니다( EvtSeek에서 지정한 오프셋에 따라 다름).
예제는 쿼리에서 책갈피 사용을 참조하세요.
다음 절차에서는 구독에서 책갈피를 사용하는 방법을 설명합니다.
구독에서 책갈피를 사용하려면
- EvtSubscribe 함수를 호출하여 쿼리와 일치하는 이벤트를 구독합니다. 책갈피 및 EvtSubscribeStartAfterBookmark 플래그에 핸들을 전달합니다.
- EVT_SUBSCRIBE_CALLBACK 함수를 구현한 경우 콜백은 책갈피가 지정된 이벤트 이후에 시작되는 이벤트를 수신합니다.
- 콜백을 구현하지 않은 경우 루프에서 EvtNext 함수를 호출하여 책갈피가 지정된 이벤트 이후에 시작되는 이벤트를 열거합니다.
예제는 구독에서 책갈피 사용을 참조하세요.
쿼리에서 책갈피 사용
다음 예제에서는 쿼리에서 책갈피를 사용하는 방법을 보여줍니다. 이 예제에서는 이벤트 쿼리의 예제를 확장합니다.
// Enumerate all the events in the result set beginning with the bookmarked event.
DWORD PrintResults(EVT_HANDLE hResults)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hEvents[ARRAY_SIZE];
DWORD dwReturned = 0;
LPWSTR pwsBookmarkXml = NULL;
EVT_HANDLE hBookmark = NULL;
// Get the persisted bookmark XML string.
pwsBookmarkXml = GetBookmarkedString();
// If the bookmark string was persisted, create a bookmark and
// seek to the bookmarked event in the result set.
if (pwsBookmarkXml)
{
hBookmark = EvtCreateBookmark(pwsBookmarkXml);
if (NULL == hBookmark)
{
wprintf(L"EvtCreateBookmark failed with %lu\n", GetLastError());
goto cleanup;
}
if (!EvtSeek(hResults, 1, hBookmark, 0, EvtSeekRelativeToBookmark))
{
wprintf(L"EvtSeek failed with %lu\n", GetLastError());
goto cleanup;
}
}
// Enumerate the events in the result set after the bookmarked event.
while (true)
{
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 (DWORD i = 0; i < dwReturned; i++)
{
if (status = PrintEvent(hEvents[i]))
{
EvtClose(hEvents[i]);
hEvents[i] = NULL;
}
else
{
goto cleanup;
}
}
}
cleanup:
if (ERROR_NO_MORE_ITEMS == status)
{
// Get the last event in the result set, use it to create a
// bookmark, render the bookmark as an XML string, and persist the string.
if (ERROR_SUCCESS != (status = SaveBookmark(hResults)))
wprintf(L"\nFailed to save bookmark\n");
}
else
{
for (DWORD i = 0; i < dwReturned; i++)
{
if (NULL != hEvents[i])
EvtClose(hEvents[i]);
}
}
if (pwsBookmarkXml)
free(pwsBookmarkXml);
return status;
}
// Get the bookmark XML string from wherever you persisted it.
LPWSTR GetBookmarkedString(void)
{
LPWSTR pwsBookmarkXML = NULL;
// TODO: Add the code to get the bookmark XML string from storage.
return pwsBookmarkXML;
}
// Persist the bookmark XML string. This example assumes that we've read through
// the result set and are persisting the last event as the bookmark.
DWORD SaveBookmark(EVT_HANDLE hResults)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
DWORD dwReturned = 0;
LPWSTR pBookmarkXml = NULL;
EVT_HANDLE hBookmark = NULL;
EVT_HANDLE hEvent = NULL;
// Seek to the last event in the result set and get the event.
if (!EvtSeek(hResults, 0, NULL, 0, EvtSeekRelativeToLast))
{
wprintf(L"EvtSeek failed with %lu\n", status = GetLastError());
goto cleanup;
}
if (!EvtNext(hResults, 1, &hEvent, INFINITE, 0, &dwReturned))
{
wprintf(L"EvtNext failed with %lu\n", status = GetLastError());
goto cleanup;
}
// Create a bookmark and update it with the last event in the result set.
hBookmark = EvtCreateBookmark(NULL);
if (NULL == hBookmark)
{
wprintf(L"EvtCreateBookmark failed with %lu\n", GetLastError());
goto cleanup;
}
if (!EvtUpdateBookmark(hBookmark, hEvent))
{
wprintf(L"EvtUpdateBookmark failed with %lu\n", GetLastError());
goto cleanup;
}
// Render the bookmark as an XML string that you can then persist.
if (!EvtRender(NULL, hBookmark, EvtRenderBookmark, dwBufferSize, pBookmarkXml, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pBookmarkXml = (LPWSTR)malloc(dwBufferSize);
if (pBookmarkXml)
{
EvtRender(NULL, hBookmark, EvtRenderBookmark, dwBufferSize, pBookmarkXml, &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;
}
}
// TODO: Add code to persist bookmark XML string.
cleanup:
if (pBookmarkXml)
free(pBookmarkXml);
if (hBookmark)
EvtClose(hBookmark);
if (hEvent)
EvtClose(hEvent);
return status;
}
구독에서 책갈피 사용
다음 예제에서는 밀어넣기 구독에서 책갈피를 사용하는 방법을 보여줍니다.
#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);
EVT_HANDLE GetBookmark();
DWORD SaveBookmark(EVT_HANDLE hBookmark);
void main(void)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hSubscription = NULL;
EVT_HANDLE hBookmark = NULL;
LPWSTR pwsPath = L"<channel name goes here>";
LPWSTR pwsQuery = L"<xpath query goes here>";
// Get the saved bookmark.
hBookmark = GetBookmark();
// Subscribe to existing and furture events beginning with the bookmarked event.
// If the bookmark has not been persisted, pass an empty bookmark and the subscription
// will begin with the second event that matches the query criteria.
hSubscription = EvtSubscribe(NULL, NULL, pwsPath, pwsQuery, hBookmark, (PVOID)hBookmark,
(EVT_SUBSCRIBE_CALLBACK)SubscriptionCallback, EvtSubscribeStartAfterBookmark);
if (NULL == hSubscription)
{
wprintf(L"EvtSubscribe failed with %lu.\n", GetLastError());
goto cleanup;
}
wprintf(L"Hit any key to quit\n\n");
while (!_kbhit())
Sleep(10);
status = SaveBookmark(hBookmark);
cleanup:
if (hSubscription)
EvtClose(hSubscription);
if (hBookmark)
EvtClose(hBookmark);
}
// The callback that receives the events that match the query criteria. Use the
// context parameter to pass the handle to the bookmark, so you can update the bookmark
// with each event that you receive.
DWORD WINAPI SubscriptionCallback(EVT_SUBSCRIBE_NOTIFY_ACTION action, PVOID pContext, EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hBookmark = (EVT_HANDLE)pContext;
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;
}
if (!EvtUpdateBookmark(hBookmark, hEvent))
{
wprintf(L"EvtUpdateBookmark failed with %lu\n", status = GetLastError());
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.
}
EVT_HANDLE GetBookmark(void)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hBookmark = NULL;
LPWSTR pBookmarkXml = NULL;
// Set pBookmarkXml to the XML string that you persisted in SaveBookmark.
hBookmark = EvtCreateBookmark(pBookmarkXml);
if (NULL == hBookmark)
{
wprintf(L"EvtCreateBookmark failed with %lu\n", GetLastError());
goto cleanup;
}
cleanup:
if (pBookmarkXml)
free(pBookmarkXml);
return hBookmark;
}
DWORD SaveBookmark(EVT_HANDLE hBookmark)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pBookmarkXml = NULL;
if (!EvtRender(NULL, hBookmark, EvtRenderBookmark, dwBufferSize, pBookmarkXml, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pBookmarkXml = (LPWSTR)malloc(dwBufferSize);
if (pBookmarkXml)
{
EvtRender(NULL, hBookmark, EvtRenderBookmark, dwBufferSize, pBookmarkXml, &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;
}
}
// Persist bookmark to a file or the registry.
cleanup:
if (pBookmarkXml)
free(pBookmarkXml);
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;
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;
}