撰寫 MOF (傳統) 事件
您必須先註冊提供者,才能將事件寫入追蹤會話。 註冊提供者會告知 ETW 您的提供者已準備好將事件寫入追蹤會話。 進程最多可以註冊 1,024 個提供者 GUID;不過,您應該將進程註冊的提供者數目限制為一或兩個。
在 Windows Vista 之前: 進程可以註冊的提供者數目沒有限制。
若要註冊傳統提供者,請呼叫 RegisterTraceGuids 函 式。 函式會註冊提供者的 GUID、事件追蹤類別 GUID,並在控制器啟用或停用提供者時識別 ETW 呼叫的回呼。
如果提供者呼叫TraceEvent函式來記錄事件,則呼叫RegisterTraceGuids函式時,您不需要包含類別 GUID 的陣列 (可以是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;
}