ETW的学习和使用也有段时间了,在网上资料都比较零散,这里就对ETW相关知识做一个整理,方便积累和使用.
一、Windows ETW基础知识
1.下面是微软的文档对于ETW可以分为三部分Controller、Provider、Consumer,Provider是事件的提供者,Controller创建一会会话打开相关的ETW,Consumer使用Controller的会话,并解析ETW数据。
二.Controller
2.1 配置打开Manifest-based或者Classic的ETW
Example that Creates a Session and Enables a Manifest-based or Classic Provider
该方法可以自定义一个LOGSESSION_NAME,通过EnableTraceEx2传入一个需要监控的ProviderGuid,并传入EVENT_CONTROL_CODE_ENABLE_PROVIDER来开启该ETW的监控,需要注意有时候不同ETW事件的开启需要的权限会不同,不同的事件ETW,可以通过EnableTraceEx2的参数来进行过滤。
可以通过logman query providers指令查询系统上所有的Provider的Guid
#include <windows.h> #include <stdio.h> #include <conio.h> #include <strsafe.h> #include <wmistr.h> #include <evntrace.h> #define LOGFILE_PATH L"<FULLPATHTOLOGFILE.etl>" #define LOGSESSION_NAME L"My Event Trace Session" // GUID that identifies your trace session. // Remember to create your own session GUID. // {AE44CB98-BD11-4069-8093-770EC9258A12} static const GUID SessionGuid = { 0xae44cb98, 0xbd11, 0x4069, { 0x80, 0x93, 0x77, 0xe, 0xc9, 0x25, 0x8a, 0x12 } }; // GUID that identifies the provider that you want // to enable to your session. // {D8909C24-5BE9-4502-98CA-AB7BDC24899D} static const GUID ProviderGuid = { 0xd8909c24, 0x5be9, 0x4502, {0x98, 0xca, 0xab, 0x7b, 0xdc, 0x24, 0x89, 0x9d } }; void wmain(void) { ULONG status = ERROR_SUCCESS; TRACEHANDLE SessionHandle = 0; EVENT_TRACE_PROPERTIES* pSessionProperties = NULL; ULONG BufferSize = 0; BOOL TraceOn = TRUE; // Allocate memory for the session properties. The memory must // be large enough to include the log file name and session name, // which get appended to the end of the session properties structure. BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(LOGSESSION_NAME); pSessionProperties = (EVENT_TRACE_PROPERTIES*) malloc(BufferSize); if (NULL == pSessionProperties) { wprintf(L"Unable to allocate %d bytes for properties structure. ", BufferSize); goto cleanup; } // Set the session properties. You only append the log file name // to the properties structure; the StartTrace function appends // the session name for you. ZeroMemory(pSessionProperties, BufferSize); pSessionProperties->Wnode.BufferSize = BufferSize; pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution pSessionProperties->Wnode.Guid = SessionGuid; pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_SEQUENTIAL; pSessionProperties->MaximumFileSize = 1; // 1 MB pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME); StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH); // Create the trace session. status = StartTrace((PTRACEHANDLE)&SessionHandle, LOGSESSION_NAME, pSessionProperties); if (ERROR_SUCCESS != status) { wprintf(L"StartTrace() failed with %lu ", status); goto cleanup; } // Enable the providers that you want to log events to your session. status = EnableTraceEx2( SessionHandle, (LPCGUID)&ProviderGuid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL ); if (ERROR_SUCCESS != status) { wprintf(L"EnableTrace() failed with %lu ", status); TraceOn = FALSE; goto cleanup; } wprintf(L"Run the provider application. Then hit any key to stop the session. "); _getch(); cleanup: if (SessionHandle) { if (TraceOn) { status = EnableTraceEx2( SessionHandle, (LPCGUID)&ProviderGuid, EVENT_CONTROL_CODE_DISABLE_PROVIDER, TRACE_LEVEL_INFORMATION, 0, 0, 0, NULL ); } status = ControlTrace(SessionHandle, LOGSESSION_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP); if (ERROR_SUCCESS != status) { wprintf(L"ControlTrace(stop) failed with %lu ", status); } } if (pSessionProperties) { free(pSessionProperties); pSessionProperties = NULL; } }
2.2 配置打开NT Kernel Logger
Configuring and Starting the NT Kernel Logger Session
这种方法StartTrace传入的必须是KERNEL_LOGGER_NAME,不需要使用EnableTraceEx2来开启,通过pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_NETWORK_TCPIP字段来指定需要关注的etw事件,可以指定监控的Flags枚举如下:

// // Event types for system configuration records // #define EVENT_TRACE_TYPE_CONFIG_CPU 0x0A // CPU Configuration #define EVENT_TRACE_TYPE_CONFIG_PHYSICALDISK 0x0B // Physical Disk Configuration #define EVENT_TRACE_TYPE_CONFIG_LOGICALDISK 0x0C // Logical Disk Configuration #define EVENT_TRACE_TYPE_CONFIG_NIC 0x0D // NIC Configuration #define EVENT_TRACE_TYPE_CONFIG_VIDEO 0x0E // Video Adapter Configuration #define EVENT_TRACE_TYPE_CONFIG_SERVICES 0x0F // Active Services #define EVENT_TRACE_TYPE_CONFIG_POWER 0x10 // ACPI Configuration #define EVENT_TRACE_TYPE_CONFIG_NETINFO 0x11 // Networking Configuration #define EVENT_TRACE_TYPE_CONFIG_IRQ 0x15 // IRQ assigned to devices #define EVENT_TRACE_TYPE_CONFIG_PNP 0x16 // PnP device info #define EVENT_TRACE_TYPE_CONFIG_IDECHANNEL 0x17 // Primary/Secondary IDE channel Configuration #define EVENT_TRACE_TYPE_CONFIG_PLATFORM 0x19 // Platform Configuration // // Enable flags for Kernel Events // #define EVENT_TRACE_FLAG_PROCESS 0x00000001 // process start & end #define EVENT_TRACE_FLAG_THREAD 0x00000002 // thread start & end #define EVENT_TRACE_FLAG_IMAGE_LOAD 0x00000004 // image load #define EVENT_TRACE_FLAG_DISK_IO 0x00000100 // physical disk IO #define EVENT_TRACE_FLAG_DISK_FILE_IO 0x00000200 // requires disk IO #define EVENT_TRACE_FLAG_MEMORY_PAGE_FAULTS 0x00001000 // all page faults #define EVENT_TRACE_FLAG_MEMORY_HARD_FAULTS 0x00002000 // hard faults only #define EVENT_TRACE_FLAG_NETWORK_TCPIP 0x00010000 // tcpip send & receive #define EVENT_TRACE_FLAG_REGISTRY 0x00020000 // registry calls #define EVENT_TRACE_FLAG_DBGPRINT 0x00040000 // DbgPrint(ex) Calls // // Enable flags for Kernel Events on Vista and above // #define EVENT_TRACE_FLAG_PROCESS_COUNTERS 0x00000008 // process perf counters #define EVENT_TRACE_FLAG_CSWITCH 0x00000010 // context switches #define EVENT_TRACE_FLAG_DPC 0x00000020 // deffered procedure calls #define EVENT_TRACE_FLAG_INTERRUPT 0x00000040 // interrupts #define EVENT_TRACE_FLAG_SYSTEMCALL 0x00000080 // system calls #define EVENT_TRACE_FLAG_DISK_IO_INIT 0x00000400 // physical disk IO initiation #define EVENT_TRACE_FLAG_ALPC 0x00100000 // ALPC traces #define EVENT_TRACE_FLAG_SPLIT_IO 0x00200000 // split io traces (VolumeManager) #define EVENT_TRACE_FLAG_DRIVER 0x00800000 // driver delays #define EVENT_TRACE_FLAG_PROFILE 0x01000000 // sample based profiling #define EVENT_TRACE_FLAG_FILE_IO 0x02000000 // file IO #define EVENT_TRACE_FLAG_FILE_IO_INIT 0x04000000 // file IO initiation // // Enable flags for Kernel Events on Win7 and above // #define EVENT_TRACE_FLAG_DISPATCHER 0x00000800 // scheduler (ReadyThread) #define EVENT_TRACE_FLAG_VIRTUAL_ALLOC 0x00004000 // VM operations // // Pre-defined Enable flags for everybody else // #define EVENT_TRACE_FLAG_EXTENSION 0x80000000 // Indicates more flags #define EVENT_TRACE_FLAG_FORWARD_WMI 0x40000000 // Can forward to WMI #define EVENT_TRACE_FLAG_ENABLE_RESERVE 0x20000000 // Reserved
#define INITGUID // Include this #define to use SystemTraceControlGuid in Evntrace.h. #include <windows.h> #include <stdio.h> #include <conio.h> #include <strsafe.h> #include <wmistr.h> #include <evntrace.h> #define LOGFILE_PATH L"<FULLPATHTOTHELOGFILE.etl>" void wmain(void) { ULONG status = ERROR_SUCCESS; TRACEHANDLE SessionHandle = 0; EVENT_TRACE_PROPERTIES* pSessionProperties = NULL; ULONG BufferSize = 0; // Allocate memory for the session properties. The memory must // be large enough to include the log file name and session name, // which get appended to the end of the session properties structure. BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(KERNEL_LOGGER_NAME); pSessionProperties = (EVENT_TRACE_PROPERTIES*) malloc(BufferSize); if (NULL == pSessionProperties) { wprintf(L"Unable to allocate %d bytes for properties structure. ", BufferSize); goto cleanup; } // Set the session properties. You only append the log file name // to the properties structure; the StartTrace function appends // the session name for you. ZeroMemory(pSessionProperties, BufferSize); pSessionProperties->Wnode.BufferSize = BufferSize; pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution pSessionProperties->Wnode.Guid = SystemTraceControlGuid; pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_NETWORK_TCPIP; pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_CIRCULAR; pSessionProperties->MaximumFileSize = 5; // 5 MB pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME); StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH); // Create the trace session. status = StartTrace((PTRACEHANDLE)&SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties); if (ERROR_SUCCESS != status) { if (ERROR_ALREADY_EXISTS == status) { wprintf(L"The NT Kernel Logger session is already in use. "); } else { wprintf(L"EnableTrace() failed with %lu ", status); } goto cleanup; } wprintf(L"Press any key to end trace session "); _getch(); cleanup: if (SessionHandle) { status = ControlTrace(SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP); if (ERROR_SUCCESS != status) { wprintf(L"ControlTrace(stop) failed with %lu ", status); } } if (pSessionProperties) free(pSessionProperties); }
三、Consumer
3.1 TdhFormatProperty
Using TdhFormatProperty to Consume Event Data
这种方法可以使用TdhFormatProperty对指定LOGGER_NAME或者LOGFILE_PATH的Etw进行解析

//Turns the DEFINE_GUID for EventTraceGuid into a const. #define INITGUID #include <windows.h> #include <stdio.h> #include <wbemidl.h> #include <wmistr.h> #include <evntrace.h> #include <tdh.h> #include <in6addr.h> #pragma comment(lib, "tdh.lib") #define LOGFILE_PATH L"C:\Code\etw\V2EventTraceController\mylogfile.etl" // Used to calculate CPU usage ULONG g_TimerResolution = 0; // Used to determine if the session is a private session or kernel session. // You need to know this when accessing some members of the EVENT_TRACE.Header // member (for example, KernelTime or UserTime). BOOL g_bUserMode = FALSE; // Handle to the trace file that you opened. TRACEHANDLE g_hTrace = 0; // Prototypes void WINAPI ProcessEvent(PEVENT_RECORD pEvent); DWORD GetEventInformation(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO & pInfo); PBYTE PrintProperties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData); DWORD GetPropertyLength(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT PropertyLength); DWORD GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT ArraySize); DWORD GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, DWORD DecodingSource, PEVENT_MAP_INFO & pMapInfo); void RemoveTrailingSpace(PEVENT_MAP_INFO pMapInfo); void wmain(void) { TDHSTATUS status = ERROR_SUCCESS; EVENT_TRACE_LOGFILE trace; TRACE_LOGFILE_HEADER* pHeader = &trace.LogfileHeader; // Identify the log file from which you want to consume events // and the callbacks used to process the events and buffers. ZeroMemory(&trace, sizeof(EVENT_TRACE_LOGFILE)); trace.LogFileName = (LPWSTR) LOGFILE_PATH; trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK) (ProcessEvent); trace.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD; g_hTrace = OpenTrace(&trace); if (INVALID_PROCESSTRACE_HANDLE == g_hTrace) { wprintf(L"OpenTrace failed with %lu ", GetLastError()); goto cleanup; } g_bUserMode = pHeader->LogFileMode & EVENT_TRACE_PRIVATE_LOGGER_MODE; if (pHeader->TimerResolution > 0) { g_TimerResolution = pHeader->TimerResolution / 10000; } wprintf(L"Number of events lost: %lu ", pHeader->EventsLost); // Use pHeader to access all fields prior to LoggerName. // Adjust pHeader based on the pointer size to access // all fields after LogFileName. This is required only if // you are consuming events on an architecture that is // different from architecture used to write the events. if (pHeader->PointerSize != sizeof(PVOID)) { pHeader = (PTRACE_LOGFILE_HEADER)((PUCHAR)pHeader + 2 * (pHeader->PointerSize - sizeof(PVOID))); } wprintf(L"Number of buffers lost: %lu ", pHeader->BuffersLost); status = ProcessTrace(&g_hTrace, 1, 0, 0); if (status != ERROR_SUCCESS && status != ERROR_CANCELLED) { wprintf(L"ProcessTrace failed with %lu ", status); goto cleanup; } cleanup: if (INVALID_PROCESSTRACE_HANDLE != g_hTrace) { status = CloseTrace(g_hTrace); } } // Callback that receives the events. VOID WINAPI ProcessEvent(PEVENT_RECORD pEvent) { DWORD status = ERROR_SUCCESS; PTRACE_EVENT_INFO pInfo = NULL; LPWSTR pwsEventGuid = NULL; PBYTE pUserData = NULL; PBYTE pEndOfUserData = NULL; DWORD PointerSize = 0; ULONGLONG TimeStamp = 0; ULONGLONG Nanoseconds = 0; SYSTEMTIME st; SYSTEMTIME stLocal; FILETIME ft; // Skips the event if it is the event trace header. Log files contain this event // but real-time sessions do not. The event contains the same information as // the EVENT_TRACE_LOGFILE.LogfileHeader member that you can access when you open // the trace. if (IsEqualGUID(pEvent->EventHeader.ProviderId, EventTraceGuid) && pEvent->EventHeader.EventDescriptor.Opcode == EVENT_TRACE_TYPE_INFO) { ; // Skip this event. } else { // Process the event. The pEvent->UserData member is a pointer to // the event specific data, if it exists. status = GetEventInformation(pEvent, pInfo); if (ERROR_SUCCESS != status) { wprintf(L"GetEventInformation failed with %lu ", status); goto cleanup; } // Determine whether the event is defined by a MOF class, in an // instrumentation manifest, or a WPP template; to use TDH to decode // the event, it must be defined by one of these three sources. if (DecodingSourceWbem == pInfo->DecodingSource) // MOF class { HRESULT hr = StringFromCLSID(pInfo->EventGuid, &pwsEventGuid); if (FAILED(hr)) { wprintf(L"StringFromCLSID failed with 0x%x ", hr); status = hr; goto cleanup; } wprintf(L" Event GUID: %s ", pwsEventGuid); CoTaskMemFree(pwsEventGuid); pwsEventGuid = NULL; wprintf(L"Event version: %d ", pEvent->EventHeader.EventDescriptor.Version); wprintf(L"Event type: %d ", pEvent->EventHeader.EventDescriptor.Opcode); } else if (DecodingSourceXMLFile == pInfo->DecodingSource) // Instrumentation manifest { wprintf(L"Event ID: %d ", pInfo->EventDescriptor.Id); } else // Not handling the WPP case { goto cleanup; } // Print the time stamp for when the event occurred. ft.dwHighDateTime = pEvent->EventHeader.TimeStamp.HighPart; ft.dwLowDateTime = pEvent->EventHeader.TimeStamp.LowPart; FileTimeToSystemTime(&ft, &st); SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal); TimeStamp = pEvent->EventHeader.TimeStamp.QuadPart; Nanoseconds = (TimeStamp % 10000000) * 100; wprintf(L"%02d/%02d/%02d %02d:%02d:%02d.%I64u ", stLocal.wMonth, stLocal.wDay, stLocal.wYear, stLocal.wHour, stLocal.wMinute, stLocal.wSecond, Nanoseconds); // If the event contains event-specific data use TDH to extract // the event data. For this example, to extract the data, the event // must be defined by a MOF class or an instrumentation manifest. // Need to get the PointerSize for each event to cover the case where you are // consuming events from multiple log files that could have been generated on // different architectures. Otherwise, you could have accessed the pointer // size when you opened the trace above (see pHeader->PointerSize). if (EVENT_HEADER_FLAG_32_BIT_HEADER == (pEvent->EventHeader.Flags & EVENT_HEADER_FLAG_32_BIT_HEADER)) { PointerSize = 4; } else { PointerSize = 8; } pUserData = (PBYTE)pEvent->UserData; pEndOfUserData = (PBYTE)pEvent->UserData + pEvent->UserDataLength; // Print the event data for all the top-level properties. Metadata for all the // top-level properties come before structure member properties in the // property information array. for (USHORT i = 0; i < pInfo->TopLevelPropertyCount; i++) { pUserData = PrintProperties(pEvent, pInfo, PointerSize, i, pUserData, pEndOfUserData); if (NULL == pUserData) { wprintf(L"Printing top level properties failed. "); goto cleanup; } } } cleanup: if (pInfo) { free(pInfo); } if (ERROR_SUCCESS != status || NULL == pUserData) { CloseTrace(g_hTrace); } } // Print the property. PBYTE PrintProperties(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, DWORD PointerSize, USHORT i, PBYTE pUserData, PBYTE pEndOfUserData) { TDHSTATUS status = ERROR_SUCCESS; USHORT PropertyLength = 0; DWORD FormattedDataSize = 0; USHORT UserDataConsumed = 0; USHORT UserDataLength = 0; LPWSTR pFormattedData = NULL; DWORD LastMember = 0; // Last member of a structure USHORT ArraySize = 0; PEVENT_MAP_INFO pMapInfo = NULL; // Get the length of the property. status = GetPropertyLength(pEvent, pInfo, i, &PropertyLength); if (ERROR_SUCCESS != status) { wprintf(L"GetPropertyLength failed. "); pUserData = NULL; goto cleanup; } // Get the size of the array if the property is an array. status = GetArraySize(pEvent, pInfo, i, &ArraySize); for (USHORT k = 0; k < ArraySize; k++) { // If the property is a structure, print the members of the structure. if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct) { LastMember = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex + pInfo->EventPropertyInfoArray[i].structType.NumOfStructMembers; for (USHORT j = pInfo->EventPropertyInfoArray[i].structType.StructStartIndex; j < LastMember; j++) { pUserData = PrintProperties(pEvent, pInfo, PointerSize, j, pUserData, pEndOfUserData); if (NULL == pUserData) { wprintf(L"Printing the members of the structure failed. "); pUserData = NULL; goto cleanup; } } } else { // Get the name/value mapping if the property specifies a value map. status = GetMapInfo(pEvent, (PWCHAR)((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].nonStructType.MapNameOffset), pInfo->DecodingSource, pMapInfo); if (ERROR_SUCCESS != status) { wprintf(L"GetMapInfo failed "); pUserData = NULL; goto cleanup; } // Get the size of the buffer required for the formatted data. status = TdhFormatProperty( pInfo, pMapInfo, PointerSize, pInfo->EventPropertyInfoArray[i].nonStructType.InType, pInfo->EventPropertyInfoArray[i].nonStructType.OutType, PropertyLength, (USHORT)(pEndOfUserData - pUserData), pUserData, &FormattedDataSize, pFormattedData, &UserDataConsumed); if (ERROR_INSUFFICIENT_BUFFER == status) { if (pFormattedData) { free(pFormattedData); pFormattedData = NULL; } pFormattedData = (LPWSTR) malloc(FormattedDataSize); if (pFormattedData == NULL) { wprintf(L"Failed to allocate memory for formatted data (size=%lu). ", FormattedDataSize); status = ERROR_OUTOFMEMORY; pUserData = NULL; goto cleanup; } // Retrieve the formatted data. status = TdhFormatProperty( pInfo, pMapInfo, PointerSize, pInfo->EventPropertyInfoArray[i].nonStructType.InType, pInfo->EventPropertyInfoArray[i].nonStructType.OutType, PropertyLength, (USHORT)(pEndOfUserData - pUserData), pUserData, &FormattedDataSize, pFormattedData, &UserDataConsumed); } if (ERROR_SUCCESS == status) { wprintf(L"%s: %s ", (PWCHAR)((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[i].NameOffset), pFormattedData); pUserData += UserDataConsumed; } else { wprintf(L"TdhFormatProperty failed with %lu. ", status); pUserData = NULL; goto cleanup; } } } cleanup: if (pFormattedData) { free(pFormattedData); pFormattedData = NULL; } if (pMapInfo) { free(pMapInfo); pMapInfo = NULL; } return pUserData; } // Get the length of the property data. For MOF-based events, the size is inferred from the data type // of the property. For manifest-based events, the property can specify the size of the property value // using the length attribute. The length attribue can specify the size directly or specify the name // of another property in the event data that contains the size. If the property does not include the // length attribute, the size is inferred from the data type. The length will be zero for variable // length, null-terminated strings and structures. DWORD GetPropertyLength(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT PropertyLength) { DWORD status = ERROR_SUCCESS; PROPERTY_DATA_DESCRIPTOR DataDescriptor; DWORD PropertySize = 0; // If the property is a binary blob and is defined in a manifest, the property can // specify the blob's size or it can point to another property that defines the // blob's size. The PropertyParamLength flag tells you where the blob's size is defined. if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamLength) == PropertyParamLength) { DWORD Length = 0; // Expects the length to be defined by a UINT16 or UINT32 DWORD j = pInfo->EventPropertyInfoArray[i].lengthPropertyIndex; ZeroMemory(&DataDescriptor, sizeof(PROPERTY_DATA_DESCRIPTOR)); DataDescriptor.PropertyName = (ULONGLONG)((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[j].NameOffset); DataDescriptor.ArrayIndex = ULONG_MAX; status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize); status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Length); *PropertyLength = (USHORT)Length; } else { if (pInfo->EventPropertyInfoArray[i].length > 0) { *PropertyLength = pInfo->EventPropertyInfoArray[i].length; } else { // If the property is a binary blob and is defined in a MOF class, the extension // qualifier is used to determine the size of the blob. However, if the extension // is IPAddrV6, you must set the PropertyLength variable yourself because the // EVENT_PROPERTY_INFO.length field will be zero. if (TDH_INTYPE_BINARY == pInfo->EventPropertyInfoArray[i].nonStructType.InType && TDH_OUTTYPE_IPV6 == pInfo->EventPropertyInfoArray[i].nonStructType.OutType) { *PropertyLength = (USHORT)sizeof(IN6_ADDR); } else if (TDH_INTYPE_UNICODESTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType || TDH_INTYPE_ANSISTRING == pInfo->EventPropertyInfoArray[i].nonStructType.InType || (pInfo->EventPropertyInfoArray[i].Flags & PropertyStruct) == PropertyStruct) { *PropertyLength = pInfo->EventPropertyInfoArray[i].length; } else { wprintf(L"Unexpected length of 0 for intype %d and outtype %d ", pInfo->EventPropertyInfoArray[i].nonStructType.InType, pInfo->EventPropertyInfoArray[i].nonStructType.OutType); status = ERROR_EVT_INVALID_EVENT_DATA; goto cleanup; } } } cleanup: return status; } // Get the size of the array. For MOF-based events, the size is specified in the declaration or using // the MAX qualifier. For manifest-based events, the property can specify the size of the array // using the count attribute. The count attribue can specify the size directly or specify the name // of another property in the event data that contains the size. DWORD GetArraySize(PEVENT_RECORD pEvent, PTRACE_EVENT_INFO pInfo, USHORT i, PUSHORT ArraySize) { DWORD status = ERROR_SUCCESS; PROPERTY_DATA_DESCRIPTOR DataDescriptor; DWORD PropertySize = 0; if ((pInfo->EventPropertyInfoArray[i].Flags & PropertyParamCount) == PropertyParamCount) { DWORD Count = 0; // Expects the count to be defined by a UINT16 or UINT32 DWORD j = pInfo->EventPropertyInfoArray[i].countPropertyIndex; ZeroMemory(&DataDescriptor, sizeof(PROPERTY_DATA_DESCRIPTOR)); DataDescriptor.PropertyName = (ULONGLONG)((PBYTE)(pInfo) + pInfo->EventPropertyInfoArray[j].NameOffset); DataDescriptor.ArrayIndex = ULONG_MAX; status = TdhGetPropertySize(pEvent, 0, NULL, 1, &DataDescriptor, &PropertySize); status = TdhGetProperty(pEvent, 0, NULL, 1, &DataDescriptor, PropertySize, (PBYTE)&Count); *ArraySize = (USHORT)Count; } else { *ArraySize = pInfo->EventPropertyInfoArray[i].count; } return status; } // Both MOF-based events and manifest-based events can specify name/value maps. The // map values can be integer values or bit values. If the property specifies a value // map, get the map. DWORD GetMapInfo(PEVENT_RECORD pEvent, LPWSTR pMapName, DWORD DecodingSource, PEVENT_MAP_INFO & pMapInfo) { DWORD status = ERROR_SUCCESS; DWORD MapSize = 0; // Retrieve the required buffer size for the map info. status = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &MapSize); if (ERROR_INSUFFICIENT_BUFFER == status) { pMapInfo = (PEVENT_MAP_INFO) malloc(MapSize); if (pMapInfo == NULL) { wprintf(L"Failed to allocate memory for map info (size=%lu). ", MapSize); status = ERROR_OUTOFMEMORY; goto cleanup; } // Retrieve the map info. status = TdhGetEventMapInformation(pEvent, pMapName, pMapInfo, &MapSize); } if (ERROR_SUCCESS == status) { if (DecodingSourceXMLFile == DecodingSource) { RemoveTrailingSpace(pMapInfo); } } else { if (ERROR_NOT_FOUND == status) { status = ERROR_SUCCESS; // This case is okay. } else { wprintf(L"TdhGetEventMapInformation failed with 0x%x. ", status); } } cleanup: return status; } // The mapped string values defined in a manifest will contain a trailing space // in the EVENT_MAP_ENTRY structure. Replace the trailing space with a null- // terminating character, so that the bit mapped strings are correctly formatted. void RemoveTrailingSpace(PEVENT_MAP_INFO pMapInfo) { DWORD ByteLength = 0; for (DWORD i = 0; i < pMapInfo->EntryCount; i++) { ByteLength = (wcslen((LPWSTR)((PBYTE)pMapInfo + pMapInfo->MapEntryArray[i].OutputOffset)) - 1) * 2; *((LPWSTR)((PBYTE)pMapInfo + (pMapInfo->MapEntryArray[i].OutputOffset + ByteLength))) = L'