Написание MOF-событий (классических)

Перед записью событий в сеанс трассировки необходимо зарегистрировать поставщика. Регистрация поставщика сообщает трассировки событий Windows, что ваш поставщик готов записывать события в сеанс трассировки. Процесс может зарегистрировать до 1024 идентификаторов GUID поставщика; Однако следует ограничить количество поставщиков, регистрирующихся процессом, до одного или двух.

До Windows Vista: Количество поставщиков, которые может зарегистрировать процесс, не ограничено.

Чтобы зарегистрировать классического поставщика, вызовите функцию RegisterTraceGuids . Функция регистрирует GUID поставщика, идентификаторы GUID класса трассировки событий и определяет обратный вызов, который вызывает трассировка событий Windows, когда контроллер включает или отключает поставщик.

Если поставщик вызывает функцию TraceEvent для регистрации событий, при вызове функции RegisterTraceGuids не нужно включать массив GUID класса (может иметь значение NULL). Массив необходимо включать только в том случае, если поставщик вызывает функцию TraceEventInstance для регистрации событий.

Windows XP и Windows 2000: Всегда необходимо включать массив GUID класса (не может иметь значение NULL).

После того как поставщик регистрирует себя и включается контроллером, поставщик может регистрировать события в сеансе трассировки контроллера.

Перед выходом поставщика вызовите функцию UnregisterTraceGuids , чтобы удалить регистрацию поставщика из трассировки событий Windows. Функция RegisterTraceGuids возвращает дескриптор регистрации, который передается функции UnregisterTraceGuids .

Если поставщик регистрирует события только в сеансе глобального средства ведения журнала, вам не нужно регистрировать поставщик в etW, так как контроллер глобального средства ведения журнала не включает или отключает поставщиков. Дополнительные сведения см. в разделе Настройка и запуск сеанса глобального средства ведения журнала.

Все классические поставщики (кроме тех, которые отслеживают события в сеансе глобального средства ведения журнала) должны реализовывать функцию ControlCallback . Поставщик использует сведения в обратном вызове, чтобы определить, включен ли он или отключен, и какие события он должен записывать.

Поставщик указывает имя функции обратного вызова при вызове функции RegisterTraceGuids для регистрации. EtW вызывает функцию обратного вызова, когда контроллер вызывает функцию EnableTrace для включения или отключения поставщика.

В реализации ControlCallback необходимо вызвать функцию GetTraceLoggerHandle , чтобы получить дескриптор сеанса; дескриптор сеанса используется при вызове функции TraceEvent . Необходимо вызывать функцию GetTraceEnableFlags или GetTraceEnableLevel в реализацииControlCallback , если поставщик использует уровень включения или флаги включения.

Поставщик может регистрировать события трассировки только в одном сеансе, но нет ничего, чтобы предотвратить включение одного поставщика несколькими контроллерами. Чтобы предотвратить перенаправление событий трассировки другим контроллером в его сеанс, вы можете добавить логику в реализацию ControlCallback , чтобы сравнить дескрипторы сеанса и игнорировать запросы включения от других контроллеров.

Чтобы регистрировать события, классические поставщики вызывают функцию TraceEvent . Событие состоит из структуры EVENT_TRACE_HEADER и всех данных, относящихся к событиям, которые добавляются в заголовок.

Заголовок должен содержать следующие сведения:

  • Элемент Size должен содержать общее количество байтов, записываемых для события (включая размер структуры EVENT_TRACE_HEADER и любых данных, относящихся к событию, которые добавляются в заголовок).

  • Член Guid должен содержать идентификатор GUID класса события (или член GuidPtr должен содержать указатель на GUID класса).

    Windows XP и Windows 2000: Guid класса должен быть зарегистрирован ранее с помощью функции RegisterTraceGuids .

  • Элемент Flags должен содержать флаг WNODE_FLAG_TRACED_GUID . Если вы указываете GUID класса с помощью элемента GuidPtr , также добавьте флаг WNODE_FLAG_USE_GUID_PTR .

  • Элемент Class.Type должен содержать тип события, если для публикации макета данных события используется MOF.

  • Член Class.Version должен содержать версию события, если для публикации макета данных события используется MOF. Версия используется для различения изменений данных о событиях. Задайте для начального номера версии значение 0.

При записи поставщика и потребителя можно использовать структуру для заполнения данных, относящихся к событиям, которые добавляются в заголовок. Однако если вы используете MOF для публикации данных, относящихся к конкретному событию, чтобы любой потребитель мог обработать событие, не следует использовать структуру для добавления данных, относящихся к событию, в заголовок. Это связано с тем, что компилятор может добавить дополнительные байты к данным, зависящим от события, для выравнивания байтов. Так как определение MOF не учитывает дополнительные байты, потребитель может получить недопустимые данные.

Необходимо выделить блок памяти для события и скопировать каждый элемент данных события в память или создать структуру, содержащую массив MOF_FIELD структур; большинство приложений создают структуру, которая включает массив MOF_FIELD структур. Убедитесь, что Header.Size отражает фактическое количество MOF_FIELD структур, которые фактически заданы перед ведением журнала события. Например, если событие содержит три поля данных, задайте для параметра Header.Size значение sizeof(EVENT_TRACE_HEADER) + (sizeof(MOF_FIELD) * 3).

Сведения о трассировке связанных событий см. в статье Написание связанных событий в комплексном сценарии.

В следующем примере показано, как вызвать функцию TraceEvent для регистрации событий. Пример ссылается на события, определенные в разделе Публикация схемы событий для классического поставщика.

#include <windows.h>
#include <stdio.h>
#include <wmistr.h>
#include <evntrace.h>

// GUID that identifies the category of events that the provider can log. 
// The GUID is also used in the event MOF class. 
// Remember to change this GUID if you copy and paste this example.

// {B49D5931-AD85-4070-B1B1-3F81F1532875}
static const GUID MyCategoryGuid = 
{ 0xb49d5931, 0xad85, 0x4070, { 0xb1, 0xb1, 0x3f, 0x81, 0xf1, 0x53, 0x28, 0x75 } };

// GUID that identifies the provider that you are registering.
// The GUID is also used in the provider MOF class. 
// Remember to change this GUID if you copy and paste this example.

// {7C214FB1-9CAC-4b8d-BAED-7BF48BF63BB3}
static const GUID ProviderGuid = 
{ 0x7c214fb1, 0x9cac, 0x4b8d, { 0xba, 0xed, 0x7b, 0xf4, 0x8b, 0xf6, 0x3b, 0xb3 } };

// Identifies the event type within the MyCategoryGuid category 
// of events to be logged. This is the same value as the EventType 
// qualifier that is defined in the event type MOF class for one of 
// the MyCategoryGuid category of events.

#define MY_EVENT_TYPE 1

// Event passed to TraceEvent

typedef struct _event
{
    EVENT_TRACE_HEADER Header;
    MOF_FIELD Data[MAX_MOF_FIELDS];  // Event-specific data
} MY_EVENT, *PMY_EVENT;

#define MAX_INDICES            3
#define MAX_SIGNATURE_LEN      32
#define EVENT_DATA_FIELDS_CNT  6

// Application data to be traced for Version 1 of the MOF class.

typedef struct _evtdata
{
    LONG Cost;
    DWORD Indices[MAX_INDICES];
    WCHAR Signature[MAX_SIGNATURE_LEN];
    BOOL IsComplete;
    GUID ID;
    DWORD Size;
}EVENT_DATA, *PEVENT_DATA;

// GUID used as the value for EVENT_DATA.ID.

// {25BAEDA9-C81A-4889-8764-184FE56750F2}
static const GUID tempID = 
{ 0x25baeda9, 0xc81a, 0x4889, { 0x87, 0x64, 0x18, 0x4f, 0xe5, 0x67, 0x50, 0xf2 } };

// Global variables to store tracing state information.

TRACEHANDLE g_SessionHandle = 0; // The handle to the session that enabled the provider.
ULONG g_EnableFlags = 0; // Determines which class of events to log.
UCHAR g_EnableLevel = 0; // Determines the severity of events to log.
BOOL g_TraceOn = FALSE;  // Used by the provider to determine whether it should log events.

ULONG WINAPI ControlCallback(WMIDPREQUESTCODE RequestCode, PVOID Context, ULONG* Reserved, PVOID Header);

// For this example to log the event, run the session example
// to enable the provider before running this example.

void wmain(void)
{
    ULONG status = ERROR_SUCCESS;
    EVENT_DATA EventData;
    MY_EVENT MyEvent; 
    TRACEHANDLE RegistrationHandle = 0; 
    TRACE_GUID_REGISTRATION EventClassGuids[] = {
        (LPGUID)&MyCategoryGuid, NULL
        };

    // Register the provider and specify the control callback function
    // that receives the enable/disable notifications.

    status = RegisterTraceGuids(
        (WMIDPREQUEST)ControlCallback,
        NULL,
        (LPGUID)&ProviderGuid,
        sizeof(EventClassGuids)/sizeof(TRACE_GUID_REGISTRATION),
        EventClassGuids,
        NULL,
        NULL,
        &RegistrationHandle
        );

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

    // Set the event-specific data.

    EventData.Cost = 32;
    EventData.ID = tempID;
    EventData.Indices[0] = 4;
    EventData.Indices[1] = 5;
    EventData.Indices[2] = 6;
    EventData.IsComplete = TRUE;
    wcscpy_s(EventData.Signature, MAX_SIGNATURE_LEN, L"Signature");
    EventData.Size = 1024;

    // Log the event if the provider is enabled and the session
    // passed a severity level value >= TRACE_LEVEL_ERROR (or zero).
    // If your provider generates more than one class of events, you
    // would also need to check g_EnableFlags.

    if (g_TraceOn && (0 == g_EnableLevel || TRACE_LEVEL_ERROR <= g_EnableLevel))
    {
        // Initialize the event data structure.

        ZeroMemory(&MyEvent, sizeof(MY_EVENT));
        MyEvent.Header.Size = sizeof(EVENT_TRACE_HEADER) + (sizeof(MOF_FIELD) * EVENT_DATA_FIELDS_CNT);
        MyEvent.Header.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
        MyEvent.Header.Guid = MyCategoryGuid;
        MyEvent.Header.Class.Type = MY_EVENT_TYPE;
        MyEvent.Header.Class.Version = 1;
        MyEvent.Header.Class.Level = g_EnableLevel;

        // Load the event data. You can also use the DEFINE_TRACE_MOF_FIELD
        // macro defined in evntrace.h to set the MOF_FIELD members. For example,
        // DEFINE_TRACE_MOF_FIELD(&EventData.Data[0], &EventData.Cost, sizeof(EventData.Cost), 0);

        MyEvent.Data[0].DataPtr = (ULONG64) &(EventData.Cost);
        MyEvent.Data[0].Length = sizeof(EventData.Cost);
        MyEvent.Data[1].DataPtr = (ULONG64) &(EventData.Indices);
        MyEvent.Data[1].Length = sizeof(EventData.Indices);
        MyEvent.Data[2].DataPtr = (ULONG64) &(EventData.Signature);
        MyEvent.Data[2].Length = (ULONG)(wcslen(EventData.Signature) + 1) * sizeof(WCHAR);
        MyEvent.Data[3].DataPtr = (ULONG64) &(EventData.IsComplete);
        MyEvent.Data[3].Length = sizeof(EventData.IsComplete);
        MyEvent.Data[4].DataPtr = (ULONG64) &(EventData.ID);
        MyEvent.Data[4].Length = sizeof(EventData.ID);
        MyEvent.Data[5].DataPtr = (ULONG64) &(EventData.Size);
        MyEvent.Data[5].Length = sizeof(EventData.Size);

        // Write the event.

        status = TraceEvent(g_SessionHandle, &(MyEvent.Header));

        if (ERROR_SUCCESS != status)
        {
            // Decide how you want to handle failures. Typically, you do not
            // want to terminate the application because you failed to
            // log an event. If the error is a memory failure, you may
            // may want to log a message to the system event log or turn
            // off logging.

            wprintf(L"TraceEvent() event failed with %lu\n", status);
            g_TraceOn = FALSE;
        }
    }

cleanup:

    if (RegistrationHandle)
    {
        UnregisterTraceGuids(RegistrationHandle);
    }
}


// The callback function that receives enable/disable notifications
// from one or more ETW sessions. Because more than one session
// can enable the provider, this example ignores requests from other 
// sessions if it is already enabled.

ULONG WINAPI ControlCallback(
    WMIDPREQUESTCODE RequestCode,
    PVOID Context,
    ULONG* Reserved, 
    PVOID Header
    )
{
    UNREFERENCED_PARAMETER(Context);
    UNREFERENCED_PARAMETER(Reserved);

    ULONG status = ERROR_SUCCESS;
    TRACEHANDLE TempSessionHandle = 0; 

    switch (RequestCode)
    {
        case WMI_ENABLE_EVENTS:  // Enable the provider.
        {
            SetLastError(0);

            // If the provider is already enabled to a provider, ignore 
            // the request. Get the session handle of the enabling session.
            // You need the session handle to call the TraceEvent function.
            // The session could be enabling the provider or it could be
            // updating the level and enable flags.

            TempSessionHandle = GetTraceLoggerHandle(Header);
            if (INVALID_HANDLE_VALUE == (HANDLE)TempSessionHandle)
            {
                wprintf(L"GetTraceLoggerHandle failed. Error code is %lu.\n", status = GetLastError());
                break;
            }

            if (0 == g_SessionHandle)
            {
                g_SessionHandle = TempSessionHandle;
            }
            else if (g_SessionHandle != TempSessionHandle)
            {
                break;
            }

            // Get the severity level of the events that the
            // session wants you to log.

            g_EnableLevel = GetTraceEnableLevel(g_SessionHandle); 
            if (0 == g_EnableLevel)
            {
                // If zero, determine whether the session passed zero
                // or an error occurred.

                if (ERROR_SUCCESS == (status = GetLastError()))
                {
                    // Decide what a zero enable level means to your provider.
                    // For this example, it means log all events.
                    ; 
                }
                else
                {
                    wprintf(L"GetTraceEnableLevel failed with, %lu.\n", status);
                    break;
                } 
            }

            // Get the enable flags that indicate the events that the
            // session wants you to log. The provider determines the
            // flags values. How it articulates the flag values and 
            // meanings to perspective sessions is up to it.

            g_EnableFlags = GetTraceEnableFlags(g_SessionHandle);
            if (0 == g_EnableFlags)
            {
                // If zero, determine whether the session passed zero
                // or an error occurred.

                if (ERROR_SUCCESS == (status = GetLastError()))
                {
                    // Decide what a zero enable flags value means to your provider.
                    ; 
                }
                else
                {
                    wprintf(L"GetTraceEnableFlags failed with, %lu.\n", status);
                    break;
                }
            }

            g_TraceOn = TRUE;
            break;
        }
 
        case WMI_DISABLE_EVENTS:  // Disable the provider.
        {
            // Disable the provider only if the request is coming from the
            // session that enabled the provider.

            TempSessionHandle = GetTraceLoggerHandle(Header);
            if (INVALID_HANDLE_VALUE == (HANDLE)TempSessionHandle)
            {
                wprintf(L"GetTraceLoggerHandle failed. Error code is %lu.\n", status = GetLastError());
                break;
            }

            if (g_SessionHandle == TempSessionHandle)
            {
                g_TraceOn = FALSE;
                g_SessionHandle = 0;
            }
            break;
        }

        default:
        {
            status = ERROR_INVALID_PARAMETER;
            break;
        }
    }

    return status;
}