Event Tracing for Windows(ETW) - 使用基于清单的提供者写事件 译(10)

Writing Manifest-based Events

原文链接
作者:Microsoft
译者:塔塔塔塔塔

在你写Event 到 跟踪Session 前必须先注册Provider。注册好的Provider会通知ETW你的Provider已经准备好去写Event到跟踪Session,您的提供程序已准备好将事件写入跟踪会话。一个进程最多可以注册1,024个Provider的GUID;但是,您应该将程序中注册的Provider的数量限制为一两个。

Windows Vista之前的版本:一个程序可以注册的Provider的数量没有限制。

要注册Manifest Provider,请调用EventRegister函数。该函数注册Provider的GUID,并标Controller启用或禁用Provider时ETW调用的可选回调函数。

在Provider退出之前,调用EventUnregister函数以从ETW中移除Provider的注册。你要通过传递EventRegister函数返回的注册句柄到EventUnregister这个函数。

在Session启用或禁用Provider时,Manifest Provider不需要实现EnableCallback函数来接收通知。这个Callback是可选用于提供信息参考。注册Provider时,无需指定或实现Callback。Manifest Provider可以简单地编写Event,而ETW将决定是否将Event记录到跟踪Session中。如果一个Event要求您执行大量工作以生成Event的数据,则可能需要先调用EventEnabled或EventProviderEnabled函数,以验证在执行工作之前将事件写入会话。

通常,如果Provider要求Controller将Provider-defined的筛选器数据传递给Provider时(请参阅EnableCallback的FilterData参数),或者Provider使用其自身注册时指定的上下文信息(Context)时,可以实现回调。(查看EventRegister的回调函数上下文‘CallbackContext’参数)。

基于清单的提供程序调用EventWriteEventWriteString函数将事件写入会话。如果Event数据是字符串,或者没有为Provider定义清单,并且Event数据是单个字符串,请调用EventWriteString函数编写Event。对于包含数字或复杂数据类型的Event数据,请调用EventWrite函数以记录该Event。

下面的示例显示如何使用EventWrite函数准备要写入的Event数据。该示例引用了在“基于清单的提供者的发布事件模式”中定义的事件。

#include <windows.h>
#include <stdio.h>
#include <evntprov.h>
#include "provider.h"  // 从Manifest(清单)生成

#define SUNDAY     0X1
#define MONDAY     0X2
#define TUESDAY    0X4
#define WEDNESDAY  0X8
#define THURSDAY   0X10
#define FRIDAY     0X20
#define SATURDAY   0X40

enum TRANSFER_TYPE {
  Download = 1,
  Upload,
  UploadReply
};

#define MAX_NAMEDVALUES          5  // 最大数组大小
#define MAX_PAYLOAD_DESCRIPTORS  9 + (2 * MAX_NAMEDVALUES)

typedef struct _namedvalue {
  LPWSTR name;
  USHORT value;
} NAMEDVALUE, *PNAMEDVALUE;

void wmain(void)
{
    DWORD status = ERROR_SUCCESS;
    REGHANDLE RegistrationHandle = NULL; 
    EVENT_DATA_DESCRIPTOR Descriptors[MAX_PAYLOAD_DESCRIPTORS]; 
    DWORD i = 0;

    // 数据加载到事件描述中

    USHORT Scores[3] = {45, 63, 21};
    ULONG pImage = (ULONG)&Scores;
    DWORD TransferType = Upload;
    DWORD Day = MONDAY | TUESDAY;
    LPWSTR Path = L"c:\\path\\folder\\file.ext";
    BYTE Cert[11] = {0x2, 0x4, 0x8, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x0, 0x1};
    PBYTE Guid = (PBYTE) &ProviderGuid;
    USHORT ArraySize = MAX_NAMEDVALUES;
    BOOL IsLocal = TRUE;
    NAMEDVALUE NamedValues[MAX_NAMEDVALUES] = { 
        {L"Bill", 1},
        {L"Bob", 2},
        {L"William", 3},
        {L"Robert", 4},
        {L"", 5}
        };

    status = EventRegister(
        &ProviderGuid,      // GUID 标识 Provider
        NULL,               // Callback 不使用
        NULL,               // Context 不使用
        &RegistrationHandle // 在调用EventWrite和EventUnregister时使用
        );

    if (ERROR_SUCCESS != status)
    {
        wprintf(L"EventRegister failed with %lu\n", status);
        goto cleanup;
    }
  
    // 加载TransferEvent 事件的描述数据 
    // 按<data>元素的顺序将数据添加到数组
    // 在事件模板中定义。 
   
    EventDataDescCreate(&Descriptors[i++], &pImage, sizeof(ULONG));
    EventDataDescCreate(&Descriptors[i++], Scores, sizeof(Scores));
    EventDataDescCreate(&Descriptors[i++], Guid, sizeof(GUID));
    EventDataDescCreate(&Descriptors[i++], Cert, sizeof(Cert));
    EventDataDescCreate(&Descriptors[i++], &IsLocal, sizeof(BOOL));
    EventDataDescCreate(&Descriptors[i++], Path, (ULONG)(wcslen(Path) + 1) * sizeof(WCHAR));
    EventDataDescCreate(&Descriptors[i++], &ArraySize, sizeof(USHORT));

    // 如果你的事件包含结构体, 你应该分别写入该结构的每个成员 
    // 如果结构体包含整型数据如:DWORD, 并且数据类型是以8字节对齐。
    // 您可以使用以下调用来编写结构,但是,建议您分别编写成员。
    //
    // EventDataDescCreate(&EvtData, struct, sizeof(struct));
    //
    // 由于此示例中的结构体数组同时包含字符串和数字,因此必须分别编写结构的每个成员。

    for (int j = 0; j < MAX_NAMEDVALUES; j++)
    {
        EventDataDescCreate(&Descriptors[i++], NamedValues[j].name, (ULONG)(wcslen(NamedValues[j].name)+1) * sizeof(WCHAR) );
        EventDataDescCreate(&Descriptors[i++], &(NamedValues[j].value), sizeof(USHORT) );
    }

    EventDataDescCreate(&Descriptors[i++], &Day, sizeof(DWORD));
    EventDataDescCreate(&Descriptors[i++], &TransferType, sizeof(DWORD));

    // 写入Event. 你不用在写Event前确认Provider是否启用。 
    // ETW将会写入到以启用的Provider,如果Provider没有启用则写入事件
    // 你需要额外执行一些工作来写入Event,否则就不会写入Event
    // 在执行额外的工作前你可能需要调用EventEnabled函数
    // EventEnabled 函数会告诉你是否启用了Provider,因此你可以知道你是否需要执行额外的工作

    status = EventWrite(
        RegistrationHandle,              // 变量来自 EventRegister
        &TransferEvent,                  // 清单生成的描述变量 EVENT_DESCRIPTOR
        (ULONG)MAX_PAYLOAD_DESCRIPTORS,  // EVENT_DATA_DESCRIPTORs 数组的大小
        &Descriptors[0]                  // 包含事件数据的描述符数组
        );

    if (status != ERROR_SUCCESS) 
    {
        wprintf(L"EventWrite failed with 0x%x", status);
    }

cleanup:

    EventUnregister(RegistrationHandle);
}

当您编译上面示例使用的清单(请参见编译工具清单)时,它将创建以下头文件(在以上示例中引用)。

#pragma once
//+
// Provider Microsoft-Windows-ETWProvider Event Count 1
//+
EXTERN_C __declspec(selectany) const GUID ProviderGuid = {0xd8909c24, 0x5be9, 0x4502, {0x98, 0xca, 0xab, 0x7b, 0xdc, 0x24, 0x89, 0x9d}};
//
// Keyword
//
#define READ_KEYWORD 0x1
#define WRITE_KEYWORD 0x2
#define LOCAL_KEYWORD 0x4
#define REMOTE_KEYWORD 0x8

//
// Event Descriptors
//
EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR TransferEvent = {0x1, 0x0, 0x0, 0x4, 0x0, 0x0, 0x5};
#define TransferEvent_value 0x1
#define MSG_Provider_Name                    0x90000001L
#define MSG_Event_WhenToTransfer             0xB0000001L
#define MSG_Map_Download                     0xD0000001L
#define MSG_Map_Upload                       0xD0000002L
#define MSG_Map_UploadReply                  0xD0000003L
#define MSG_Map_Sunday                       0xF0000001L
#define MSG_Map_Monday                       0xF0000002L
#define MSG_Map_Tuesday                      0xF0000003L
#define MSG_Map_Wednesday                    0xF0000004L
#define MSG_Map_Thursday                     0xF0000005L
#define MSG_Map_Friday                       0xF0000006L
#define MSG_Map_Saturday                     0xF0000007L
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值