Menulis Acara MOF (Klasik)
Sebelum dapat menulis peristiwa ke sesi pelacakan, Anda harus mendaftarkan penyedia Anda. Mendaftarkan penyedia memberi tahu ETW bahwa penyedia Anda siap untuk menulis peristiwa ke sesi pelacakan. Proses dapat mendaftar hingga 1.024 GUID penyedia; namun, Anda harus membatasi jumlah penyedia yang didaftarkan proses Anda ke satu atau dua.
Sebelum Windows Vista: Tidak ada batasan jumlah penyedia yang dapat didaftarkan oleh proses.
Untuk mendaftarkan penyedia klasik, panggil fungsi RegisterTraceGuids . Fungsi ini mendaftarkan GUID penyedia, GUID kelas pelacakan peristiwa, dan mengidentifikasi panggilan balik yang dipanggil ETW saat pengontrol mengaktifkan atau menonaktifkan penyedia.
Jika penyedia memanggil fungsi TraceEvent untuk mencatat peristiwa, Anda tidak perlu menyertakan array GUID kelas (bisa NULL) saat memanggil fungsi RegisterTraceGuids . Anda hanya perlu menyertakan array jika penyedia memanggil fungsi TraceEventInstance untuk mencatat peristiwa.
Windows XP dan Windows 2000: Anda selalu perlu menyertakan array GUID kelas (tidak boleh NULL).
Setelah penyedia mendaftarkan dirinya sendiri dan diaktifkan oleh pengontrol, penyedia dapat mencatat peristiwa ke sesi pelacakan pengontrol.
Sebelum penyedia keluar, panggil fungsi UnregisterTraceGuids untuk menghapus pendaftaran penyedia dari ETW. Fungsi RegisterTraceGuids mengembalikan handel pendaftaran yang Anda berikan ke fungsi UnregisterTraceGuids .
Jika penyedia Anda mencatat peristiwa ke sesi Global Logger saja, Anda tidak perlu mendaftarkan penyedia Anda dengan ETW karena pengontrol Global Logger tidak mengaktifkan atau menonaktifkan penyedia. Untuk detailnya, lihat Mengonfigurasi dan Memulai Sesi Pencatat Global.
Semua penyedia klasik (kecuali yang melacak peristiwa ke sesi Global Logger) harus menerapkan fungsi ControlCallback . Penyedia menggunakan informasi dalam panggilan balik untuk menentukan apakah itu diaktifkan atau dinonaktifkan dan peristiwa mana yang harus ditulisnya.
Penyedia menentukan nama fungsi panggilan balik saat memanggil fungsi RegisterTraceGuids untuk mendaftarkan dirinya sendiri. ETW memanggil fungsi panggilan balik saat pengontrol memanggil fungsi EnableTrace untuk mengaktifkan atau menonaktifkan penyedia.
Dalam implementasi ControlCallback , Anda harus memanggil fungsi GetTraceLoggerHandle untuk mengambil handel sesi; Anda menggunakan handel sesi saat memanggil fungsi TraceEvent . Anda hanya perlu memanggil fungsi GetTraceEnableFlags atau fungsi GetTraceEnableLevel dalam implementasiControlCallback Anda jika penyedia Anda menggunakan tingkat aktifkan atau aktifkan bendera.
Penyedia dapat mencatat peristiwa pelacakan hanya ke satu sesi, tetapi tidak ada yang mencegah beberapa pengontrol mengaktifkan satu penyedia. Untuk mencegah pengontrol lain mengalihkan peristiwa pelacakan Anda ke sesinya, Anda mungkin ingin menambahkan logika ke implementasi ControlCallback Anda untuk membandingkan handel sesi dan mengabaikan permintaan aktif dari pengontrol lain.
Untuk mencatat peristiwa, penyedia klasik memanggil fungsi TraceEvent . Peristiwa terdiri dari struktur EVENT_TRACE_HEADER dan data khusus peristiwa apa pun yang ditambahkan ke header .
Header harus berisi informasi berikut:
Anggota Ukuran harus berisi jumlah total byte yang akan direkam untuk peristiwa (termasuk ukuran struktur EVENT_TRACE_HEADER dan data khusus peristiwa apa pun yang ditambahkan ke header).
Anggota Guid harus berisi GUID kelas peristiwa (atau anggota GuidPtr harus berisi pointer ke GUID kelas).
Windows XP dan Windows 2000: GUID kelas harus telah terdaftar sebelumnya menggunakan fungsi RegisterTraceGuids .
Anggota Bendera harus berisi bendera WNODE_FLAG_TRACED_GUID . Jika Anda menentukan GUID kelas menggunakan anggota GuidPtr , tambahkan juga bendera WNODE_FLAG_USE_GUID_PTR .
Anggota Class.Type harus berisi jenis peristiwa, jika Anda menggunakan MOF untuk menerbitkan tata letak data peristiwa Anda.
Anggota Class.Version harus berisi versi peristiwa, jika Anda menggunakan MOF untuk menerbitkan tata letak data peristiwa Anda. Versi ini digunakan untuk membedakan antara revisi ke data peristiwa. Atur nomor versi awal ke 0.
Jika Anda menulis penyedia dan konsumen, Anda dapat menggunakan struktur untuk mengisi data khusus peristiwa yang ditambahkan ke header . Namun, jika Anda menggunakan MOF untuk menerbitkan data khusus peristiwa sehingga setiap konsumen dapat memproses peristiwa, Anda tidak boleh menggunakan struktur untuk menambahkan data khusus peristiwa ke header. Ini karena pengkompilasi dapat menambahkan byte tambahan ke data khusus peristiwa untuk tujuan perataan byte. Karena definisi MOF tidak memperhitungkan byte tambahan, konsumen dapat mengambil data yang tidak valid.
Anda harus mengalokasikan blok memori untuk peristiwa dan menyalin setiap item data peristiwa ke memori, atau membuat struktur yang menyertakan array struktur MOF_FIELD ; sebagian besar aplikasi akan membuat struktur yang mencakup array struktur MOF_FIELD . Pastikan Header.Size mencerminkan jumlah aktual struktur MOF_FIELD yang benar-benar diatur sebelum mencatat peristiwa. Misalnya, jika peristiwa berisi tiga bidang data, atur Header.Size ke sizeof(EVENT_TRACE_HEADER) + (sizeof(MOF_FIELD) * 3).
Untuk informasi tentang melacak peristiwa terkait, lihat Menulis Peristiwa Terkait dalam Skenario End-to-End.
Contoh berikut menunjukkan cara memanggil fungsi TraceEvent untuk mencatat peristiwa. Contoh mereferensikan peristiwa yang ditentukan dalam Menerbitkan Skema Peristiwa Anda untuk Penyedia Klasik.
#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;
}