다음을 통해 공유


이벤트 쿼리

채널 또는 로그 파일에서 이벤트를 쿼리할 수 있습니다. 채널 또는 로그 파일은 로컬 컴퓨터 또는 원격 컴퓨터에 있을 수 있습니다. 채널 또는 로그 파일에서 가져올 이벤트를 지정하려면 XPath 쿼리 또는 구조 XML 쿼리를 사용합니다. 쿼리 작성에 대한 자세한 내용은 이벤트 소비를 참조하세요.

이벤트를 쿼리하려면 EvtQuery 함수를 호출합니다. 이벤트가 반환되는 순서(가장 오래된 것에서 최신(기본값) 또는 가장 오래된 값)과 쿼리에서 잘못된 형식의 XPath 식을 허용할지 여부를 지정할 수 있습니다(함수가 형식이 잘못된 식을 무시하는 방법에 대한 자세한 내용은 EvtQueryTolerateQueryErrors 플래그 참조).

다음 예제에서는 XPath 식을 사용하여 채널에서 이벤트를 쿼리하는 방법을 보여 줍니다.

#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <winevt.h>

#pragma comment(lib, "wevtapi.lib")

#define ARRAY_SIZE 10
#define TIMEOUT 1000  // 1 second; Set and use in place of INFINITE in EvtNext call

DWORD PrintResults(EVT_HANDLE hResults);
DWORD PrintEvent(EVT_HANDLE hEvent); // Shown in the Rendering Events topic

void main(void)
{
    DWORD status = ERROR_SUCCESS;
    EVT_HANDLE hResults = NULL;
    LPWSTR pwsPath = L"<Name of the channel goes here>";
    LPWSTR pwsQuery = L"Event/System[EventID=4]";

    hResults = EvtQuery(NULL, pwsPath, pwsQuery, EvtQueryChannelPath | EvtQueryReverseDirection);
    if (NULL == hResults)
    {
        status = GetLastError();

        if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
            wprintf(L"The channel was not found.\n");
        else if (ERROR_EVT_INVALID_QUERY == status)
            // You can call the EvtGetExtendedStatus function to try to get 
            // additional information as to what is wrong with the query.
            wprintf(L"The query is not valid.\n");
        else
            wprintf(L"EvtQuery failed with %lu.\n", status);

        goto cleanup;
    }

    PrintResults(hResults);

cleanup:

    if (hResults)
        EvtClose(hResults);

}

다음 예제에서는 구조적 XML 쿼리를 사용하여 채널에서 이벤트를 쿼리하는 방법을 보여 줍니다. 이벤트는 최신에서 가장 오래된 순서로 반환됩니다.

#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <winevt.h>

#pragma comment(lib, "wevtapi.lib")

#define ARRAY_SIZE 10
#define TIMEOUT 1000  // 1 second; Set and use in place of INFINITE in EvtNext call

// The structured XML query.
#define QUERY \
    L"<QueryList>" \
    L"  <Query Path='Name of the channel goes here'>" \
    L"    <Select>Event/System[EventID=4]</Select>" \
    L"  </Query>" \
    L"</QueryList>"

DWORD PrintQueryStatuses(EVT_HANDLE hResults);
DWORD GetQueryStatusProperty(EVT_QUERY_PROPERTY_ID Id, EVT_HANDLE hResults, PEVT_VARIANT& pProperty);
DWORD PrintResults(EVT_HANDLE hResults);
DWORD PrintEvent(EVT_HANDLE hEvent);  // Shown in the Rendering Events topic

void main(void)
{
    DWORD status = ERROR_SUCCESS;
    EVT_HANDLE hResults = NULL;

    hResults = EvtQuery(NULL, NULL, QUERY, EvtQueryChannelPath | EvtQueryTolerateQueryErrors);
    if (NULL == hResults)
    {
        // Handle error.
        goto cleanup;
    }

    // Print the status of each query. If all the queries succeeded,
    // print the events in the result set. The status can be
    // ERROR_EVT_CHANNEL_NOT_FOUND or ERROR_EVT_INVALID_QUERY among others.
    if (ERROR_SUCCESS == PrintQueryStatuses(hResults))
        PrintResults(hResults);

cleanup:

    if (hResults)
        EvtClose(hResults);

}

구조화된 XML 쿼리를 사용하고 EvtQueryTolerateQueryErrors 플래그를 EvtQuery에 전달하는 경우 구조화된 쿼리의 쿼리 중 하나 이상이 실제로 실패했을 수 있지만 함수는 성공합니다. 구조화된 쿼리에서 성공하거나 실패한 쿼리를 확인하려면 EvtGetQueryInfo 함수를 호출합니다. EvtQueryTolerateQueryErrors 플래그를 전달하지 않으면 쿼리에서 찾은 첫 번째 오류와 함께 EvtQuery 함수가 실패합니다. ERROR_EVT_INVALID_QUERY 쿼리가 실패하면 EvtGetExtendedStatus 함수를 호출하여 XPath 오류를 설명하는 메시지 문자열을 가져옵니다.

다음 예제에서는 EvtQueryTolerateQueryErrors 플래그를 EvtQuery에 전달할 때 구조화된 쿼리에서 각 쿼리의 성공 또는 실패를 확인하는 방법을 보여 줍니다.

// Get the list of paths in the query and the status for each path. Return
// the sum of the statuses, so the caller can decide whether to enumerate 
// the results.
DWORD PrintQueryStatuses(EVT_HANDLE hResults)
{
    DWORD status = ERROR_SUCCESS;
    PEVT_VARIANT pPaths = NULL;
    PEVT_VARIANT pStatuses = NULL;

    wprintf(L"List of channels/logs that were queried and their status\n\n");

    if (status = GetQueryStatusProperty(EvtQueryNames, hResults, pPaths))
        goto cleanup;

    if (status = GetQueryStatusProperty(EvtQueryStatuses, hResults, pStatuses))
        goto cleanup;

    for (DWORD i = 0; i < pPaths->Count; i++)
    {
        wprintf(L"%s (%lu)\n", pPaths->StringArr[i], pStatuses->UInt32Arr[i]);
        status += pStatuses->UInt32Arr[i];
    }

cleanup:

    if (pPaths)
        free(pPaths);

    if (pStatuses)
        free(pStatuses);

    return status;
}


// Get the list of paths specified in the query or the list of status values 
// for each path.
DWORD GetQueryStatusProperty(EVT_QUERY_PROPERTY_ID Id, EVT_HANDLE hResults, PEVT_VARIANT& pProperty)
{
    DWORD status = ERROR_SUCCESS;
    DWORD dwBufferSize = 0;
    DWORD dwBufferUsed = 0;

    if  (!EvtGetQueryInfo(hResults, Id, dwBufferSize, pProperty, &dwBufferUsed))
    {
        status = GetLastError();
        if (ERROR_INSUFFICIENT_BUFFER == status)
        {
            dwBufferSize = dwBufferUsed;
            pProperty = (PEVT_VARIANT)malloc(dwBufferSize);
            if (pProperty)
            {
                EvtGetQueryInfo(hResults, Id, dwBufferSize, pProperty, &dwBufferUsed);
            }
            else
            {
                wprintf(L"realloc failed\n");
                status = ERROR_OUTOFMEMORY;
                goto cleanup;
            }
        }

        if (ERROR_SUCCESS != (status = GetLastError()))
        {
            wprintf(L"EvtGetQueryInfo failed with %d\n", GetLastError());
            goto cleanup;
        }
    }

cleanup:

    return status;
}

결과 집합에서 이벤트 읽기

결과 집합의 이벤트를 열거하려면 함수가 FALSE를 반환하고 GetLastError 함수가 ERROR_NO_MORE_ITEMS 반환할 때까지 루프에서 EvtNext 함수를 호출합니다. 결과 집합의 이벤트는 정적이지 않습니다. 채널에 기록되는 새 이벤트는 ERROR_NO_MORE_ITEMS 설정될 때까지 결과 집합에 포함됩니다. 성능을 향상시키려면 결과 집합의 이벤트를 일괄 처리로 페치합니다(가져올 이벤트 수를 결정할 때 각 이벤트의 크기를 고려).

다음 예제에서는 결과 집합에서 이벤트를 열거하는 방법을 보여 줍니다.

// Enumerate all the events in the result set. 
DWORD PrintResults(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. PrintEvent is shown in RenderingEvents.
        for (DWORD i = 0; i < dwReturned; i++)
        {
            if (ERROR_SUCCESS == (status = PrintEvent(hEvents[i])))
            {
                EvtClose(hEvents[i]);
                hEvents[i] = NULL;
            }
            else
            {
                goto cleanup;
            }
        }
    }

cleanup:

    for (DWORD i = 0; i < dwReturned; i++)
    {
        if (NULL != hEvents[i])
            EvtClose(hEvents[i]);
    }

    return status;
}

결과 집합에서 가져오는 이벤트를 렌더링하는 자세한 내용은 이벤트 렌더링을 참조하세요.

중단한 위치에서 이벤트를 쿼리하려면 마지막으로 읽은 이벤트의 책갈피를 만들고 다음에 쿼리를 실행할 때 사용합니다. 자세한 내용은 이벤트 책갈피 지정을 참조하세요.