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 함수를 호출하여 자신을 등록할 때 콜백 함수의 이름을 지정합니다. ETW는 컨트롤러가 EnableTrace 함수를 호출하여 공급자를 사용하거나 사용하지 않도록 설정할 때 콜백 함수를 호출합니다.
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;
}