编写 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;
}