MOF (クラシック) イベントの書き込み

トレース セッションにイベントを書き込む前に、プロバイダーを登録する必要があります。 プロバイダーを登録すると、プロバイダーがトレース セッションにイベントを書き込む準備ができていることが ETW に通知されます。 プロセスでは、最大 1,024 個のプロバイダー GUID を登録できます。ただし、プロセスで登録するプロバイダーの数を 1 つまたは 2 つに制限する必要があります。

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 関数を呼び出す必要があるのは、プロバイダーが有効化レベルまたは有効化フラグを使用している場合のみです。

プロバイダーはトレース イベントを 1 つのセッションにのみログに記録できますが、複数のコントローラーが 1 つのプロバイダーを有効にできないようにするものはありません。 別のコントローラーがトレース イベントをセッションにリダイレクトしないようにするには、 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構造体の実際の数が反映されていることを確認します。 たとえば、イベントに 3 つのデータ フィールドが含まれている場合は、 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;
}