编写 MOF (经典) 事件

必须先注册提供程序,然后才能将事件写入跟踪会话。 注册提供程序会告知 ETW 提供程序已准备好将事件写入跟踪会话。 一个进程最多可以注册 1,024 个提供程序 GUID;但是,应将进程注册的提供程序数限制为一到两个。

在 Windows Vista 之前: 进程可以注册的提供程序数量没有限制。

若要注册经典提供程序,请调用 RegisterTraceGuids 函数。 函数注册提供程序的 GUID 和事件跟踪类 GUID,并标识当控制器启用或禁用提供程序时 ETW 调用的回调。

如果提供程序调用 TraceEvent 函数来记录事件,则无需包含类 GUID 的数组, (调用 RegisterTraceGuids 函数时可为 NULL) 。 仅当提供程序调用 TraceEventInstance 函数来记录事件时,才需要包含 数组。

Windows XP 和 Windows 2000: 始终需要包含类 GUID 的数组, (不能为 NULL) 。

提供程序注册自身并由控制器启用后,该提供程序可以将事件记录到控制器的跟踪会话中。

在提供程序退出之前,调用 UnregisterTraceGuids 函数以从 ETW 中删除提供程序的注册。 RegisterTraceGuids 函数返回传递给 UnregisterTraceGuids 函数的注册句柄。

如果提供程序仅将事件记录到全局记录器会话,则无需向 ETW 注册提供程序,因为全局记录器控制器不会启用或禁用提供程序。 有关详细信息,请参阅 配置和启动全局记录器会话

除了将事件跟踪到全局记录器会话) 的所有 经典 提供程序 (都必须实现 ControlCallback 函数。 提供程序使用回调中的信息来确定它是启用或禁用的,以及它应写入哪些事件。

提供程序在调用 RegisterTraceGuids 函数以注册自身时指定回调函数的名称。 当控制器调用 EnableTrace 函数以启用或禁用提供程序时,ETW 调用回调函数。

ControlCallback 实现中,必须调用 GetTraceLoggerHandle 函数来检索会话句柄;调用 TraceEvent 函数时使用会话句柄。 如果提供程序使用启用级别或启用标志,则只需在ControlCallback 实现中调用 GetTraceEnableFlags 函数或 GetTraceEnableLevel 函数。

提供程序只能将跟踪事件记录到一个会话,但没有任何内容可以阻止多个控制器启用单个提供程序。 若要防止另一个控制器将跟踪事件重定向到其会话,可能需要向 ControlCallback 实现添加逻辑,以比较会话句柄并忽略来自其他控制器的启用请求。

若要记录事件, 经典 提供程序调用 TraceEvent 函数。 事件由 EVENT_TRACE_HEADER 结构和追加到 标头的任何特定于事件的数据组成。

标头必须包含以下信息:

  • Size 成员必须包含要为事件记录的总字节数 (包括EVENT_TRACE_HEADER结构和追加到标头) 的任何特定于事件的数据的大小。

  • Guid 成员必须包含事件 (的类 GUID,或者 GuidPtr 成员必须包含指向类 GUID) 的指针。

    Windows XP 和 Windows 2000: 类 GUID 必须以前已使用 RegisterTraceGuids 函数注册。

  • Flags 成员必须包含 WNODE_FLAG_TRACED_GUID 标志。 如果使用 GuidPtr 成员指定类 GUID,则还要添加 WNODE_FLAG_USE_GUID_PTR 标志。

  • 如果使用 MOF 发布事件数据的布局, 则 Class.Type 成员必须包含事件类型。

  • 如果使用 MOF 发布事件数据的布局, 则 Class.Version 成员必须包含事件版本。 版本用于区分事件数据的修订。 将初始版本号设置为 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;
}