You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python调用WinAPI解析ETL文件:ProcessTrace未触发回调求助

用Python调用WinAPI解析ETL文件时ProcessTrace未触发回调的问题排查

我需要用Windows原生WinAPI解析ETL文件(如sample.etl),C++下可正常解析,但改用Python调用相同API时,能通过OpenTraceA打开文件,却发现ProcessTrace未触发注册的回调函数。以下是我的代码:

import ctypes
from ctypes.wintypes import *

advapidll = "C:\Windows\System32\advapi32.dll"
advapi32 = ctypes.WinDLL(advapidll)

ERROR_SUCCESS = 0

PROCESS_TRACE_MODE_EVENT_RECORD = int(hex(10000000), 16)

class _GUID(ctypes.Structure):
    _fields_ = [
        ("Data1", ULONG),
        ("Data2", USHORT),
        ("Data3", USHORT),
        ("Data4", BYTE * 8),
    ]

class EVENT_TRACE_HEADER(ctypes.Structure):
    _fields_ = [
        # Define fields for EVENT_TRACE_HEADER here
    ]

class ETW_BUFFER_CONTEXT(ctypes.Structure):
    _fields_ = [
        ("ProcessorIndex", USHORT),
        ("LoggerId", USHORT),
    ]

class EVENT_TRACE(ctypes.Structure):
    _fields_ = [
        ("Header", EVENT_TRACE_HEADER),
        ("InstanceId", ULONG),
        ("ParentInstanceId", ULONG),
        ("ParentGuid", _GUID),  # Using UUID to represent GUID
        ("MofData", LPVOID),
        ("MofLength", ULONG),
        ("DUMMYUNIONNAME", ULONG),  # This can hold either ClientContext or BufferContext
    ]

class SYSTEMTIME(ctypes.Structure):
    _fields_ = [
        ("wYear", WORD),
        ("wMonth", WORD),
        ("wDayOfWeek", WORD),
        ("wDay", WORD),
        ("wHour", WORD),
        ("wMinute", WORD),
        ("wSecond", WORD),
        ("wMilliseconds", WORD),
    ]

class TIME_ZONE_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("Bias", LONG),
        ("StandardName", WCHAR * 32),  # WCHAR array for StandardName
        ("StandardDate", SYSTEMTIME),
        ("StandardBias", LONG),
        ("DaylightName", WCHAR * 32),  # WCHAR array for DaylightName
        ("DaylightDate", SYSTEMTIME),
        ("DaylightBias", LONG),
    ]

class _TRACE_LOGFILE_HEADER(ctypes.Structure):
    _fields_ = [
        ("BufferSize", ULONG),
        ("Version", ULONG),  # Holds either Version or VersionDetail
        ("ProviderVersion", ULONG),
        ("NumberOfProcessors", ULONG),
        ("EndTime", LARGE_INTEGER),
        ("TimerResolution", ULONG),
        ("MaximumFileSize", ULONG),
        ("LogFileMode", ULONG),
        ("BuffersWritten", ULONG),
        ("LogInstanceGuid", _GUID),  # Holds either LogInstanceGuid or DUMMYSTRUCTNAME
        ("LoggerName", LPWSTR),  # Assuming wchar_t* for wide character string
        ("LogFileName", LPWSTR),  # Assuming wchar_t* for wide character string
        ("TimeZone", TIME_ZONE_INFORMATION),  # Use TIME_ZONE_INFORMATION as per your need
        ("BootTime", LARGE_INTEGER),
        ("PerfFreq", LARGE_INTEGER),
        ("StartTime", LARGE_INTEGER),
        ("ReservedFlags", ULONG),
        ("BuffersLost", ULONG),
    ]

class EVENT_DESCRIPTOR(ctypes.Structure):
    _fields_ = [
        ("Id", USHORT),      # USHORT
        ("Version", CHAR),  # UCHAR
        ("Channel", CHAR),  # UCHAR
        ("Level", CHAR),    # UCHAR
        ("Opcode", CHAR),    # UCHAR
        ("Task", USHORT),    # USHORT
        ("Keyword", ULARGE_INTEGER)  # ULONGLONG
    ]

class EVENT_HEADER(ctypes.Structure):
    _fields_ = [
        ("Size", USHORT),
        ("HeaderType", USHORT),
        ("Flags", USHORT),
        ("EventProperty", USHORT),
        ("ThreadId", ULONG),
        ("ProcessId", ULONG),
        ("TimeStamp", LARGE_INTEGER),
        ("ProviderId", _GUID),
        ("EventDescriptor", EVENT_DESCRIPTOR),
        ("ProcessorTime", ULARGE_INTEGER),
        ("ActivityId", _GUID),
    ]

class EVENT_RECORD(ctypes.Structure):
    _fields_ = [
        ("EventHeader", EVENT_HEADER),
        ("BufferContext", ETW_BUFFER_CONTEXT),
        ("ExtendedDataCount", USHORT),
        ("UserDataLength", USHORT),
        ("ExtendedData", ctypes.POINTER(ULONG)),  # Assuming PEVENT_HEADER_EXTENDED_DATA_ITEM is a pointer to ULONG
        ("UserData", LPVOID),
        ("UserContext", LPVOID),
    ]

PEVENT_RECORD_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.POINTER(EVENT_RECORD))

class _EVENT_TRACE_LOGFILEA(ctypes.Structure):
    _fields_ = [
        ("LogFileName", LPSTR),
        ("LoggerName", LPSTR),
        ("CurrentTime", LARGE_INTEGER),
        ("BuffersRead", ULONG),
        ("ProcessTraceMode", ULONG),
        ("CurrentEvent", EVENT_TRACE),
        ("LogfileHeader", _TRACE_LOGFILE_HEADER),
        ("BufferCallback", ctypes.WINFUNCTYPE(None)), 
        ("BufferSize", ULONG),
        ("Filled", ULONG),
        ("EventsLost", ULONG),
        ("EventRecordCallback", PEVENT_RECORD_CALLBACK),  # This can hold either EventCallback or EventRecordCallback
        ("IsKernelTrace", ULONG),
        ("Context", LPVOID),
    ]

@PEVENT_RECORD_CALLBACK
def handle_trace_event(event_record):
    print("hi")

OpenTraceA = advapi32.OpenTraceA
OpenTraceA.argtypes = [ctypes.POINTER(_EVENT_TRACE_LOGFILEA)]
OpenTraceA.restype = HANDLE

ProcessTrace = advapi32.ProcessTrace
ProcessTrace.argtypes = [ctypes.POINTER(HANDLE), ULONG, LPVOID, LPVOID]
ProcessTrace.restype = DWORD

def main():
    trace_logfile = _EVENT_TRACE_LOGFILEA()
    trace_logfile.LogFileName = b"sample.etl"
    trace_logfile.EventRecordCallback = handle_trace_event
    trace_logfile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD

    trace_handle = OpenTraceA(ctypes.byref(trace_logfile))

    if trace_handle == HANDLE(0):
        print("Failed to open trace")
    else:
        print(f"Trace opened successfully: {trace_handle}")

    status = ProcessTrace(ctypes.byref(HANDLE(trace_handle)), 1, 0, 0)
    if (status != ERROR_SUCCESS):
        print(f"Problem occurred during ProcessTrace Status: {status}")

if __name__ == '__main__':
    main()

问题排查与修复

1. 未完整定义EVENT_TRACE_HEADER结构体

这是核心问题:EVENT_TRACE结构体包含EVENT_TRACE_HEADER,但你只声明了结构体却未定义字段,导致内存布局完全错误。WinAPI无法正确填充数据,自然无法触发回调。根据官方文档,EVENT_TRACE_HEADER的正确定义如下:

class EVENT_TRACE_HEADER(ctypes.Structure):
    _fields_ = [
        ("Size", USHORT),
        ("FieldTypeFlags", USHORT),
        ("Type", UCHAR),
        ("Level", UCHAR),
        ("Version", UCHAR),
        ("HeaderType", UCHAR),
        ("Flags", USHORT),
        ("ThreadId", ULONG),
        ("ProcessId", ULONG),
        ("TimeStamp", LARGE_INTEGER),
        ("Guid", _GUID),
        ("KernelTime", ULONG),
        ("UserTime", ULONG),
        ("InstanceId", ULONG),
    ]

2. _EVENT_TRACE_LOGFILEA的字段错误

  • BufferCallback类型错误:官方定义为PEVENT_TRACE_BUFFER_CALLBACK,即ctypes.WINFUNCTYPE(BOOL, ctypes.POINTER(EVENT_TRACE)),不是无参数函数类型,不需要时可设为None。
  • 结构体字段顺序必须严格匹配WinAPI定义,否则会导致内存偏移错误。

3. ProcessTrace参数传递错误

ProcessTrace的第一个参数是HANDLE数组的指针,不是单个HANDLE的指针。正确做法是创建HANDLE数组,将trace_handle放入后传递数组指针:

# 替换原ProcessTrace调用部分
handles = (HANDLE * 1)(trace_handle)
status = ProcessTrace(handles, 1, 0, 0)

4. EVENT_RECORD的ExtendedData类型错误

ExtendedData应该是指向EVENT_HEADER_EXTENDED_DATA_ITEM的指针,需要先定义该结构体:

class EVENT_HEADER_EXTENDED_DATA_ITEM(ctypes.Structure):
    _fields_ = [
        ("Reserved1", USHORT),
        ("ExtType", USHORT),
        ("DataSize", USHORT),
        ("Reserved2", USHORT),
        ("DataPtr", LPVOID),
    ]

# 修正EVENT_RECORD的ExtendedData字段
class EVENT_RECORD(ctypes.Structure):
    _fields_ = [
        ("EventHeader", EVENT_HEADER),
        ("BufferContext", ETW_BUFFER_CONTEXT),
        ("ExtendedDataCount", USHORT),
        ("UserDataLength", USHORT),
        ("ExtendedData", ctypes.POINTER(EVENT_HEADER_EXTENDED_DATA_ITEM)),
        ("UserData", LPVOID),
        ("UserContext", LPVOID),
    ]

5. 其他细节优化

  • PROCESS_TRACE_MODE_EVENT_RECORD可直接用十六进制值0x10000000,无需多余转换。
  • 确保回调函数引用被保留,避免Python垃圾回收机制回收(装饰器已处理,也可显式保留)。

修复后的完整代码

import ctypes
from ctypes.wintypes import *

advapidll = "C:\\Windows\\System32\\advapi32.dll"
advapi32 = ctypes.WinDLL(advapidll)

ERROR_SUCCESS = 0
PROCESS_TRACE_MODE_EVENT_RECORD = 0x10000000

class _GUID(ctypes.Structure):
    _fields_ = [
        ("Data1", ULONG),
        ("Data2", USHORT),
        ("Data3", USHORT),
        ("Data4", BYTE * 8),
    ]

class EVENT_TRACE_HEADER(ctypes.Structure):
    _fields_ = [
        ("Size", USHORT),
        ("FieldTypeFlags", USHORT),
        ("Type", UCHAR),
        ("Level", UCHAR),
        ("Version", UCHAR),
        ("HeaderType", UCHAR),
        ("Flags", USHORT),
        ("ThreadId", ULONG),
        ("ProcessId", ULONG),
        ("TimeStamp", LARGE_INTEGER),
        ("Guid", _GUID),
        ("KernelTime", ULONG),
        ("UserTime", ULONG),
        ("InstanceId", ULONG),
    ]

class ETW_BUFFER_CONTEXT(ctypes.Structure):
    _fields_ = [
        ("ProcessorIndex", USHORT),
        ("LoggerId", USHORT),
    ]

class EVENT_TRACE(ctypes.Structure):
    _fields_ = [
        ("Header", EVENT_TRACE_HEADER),
        ("InstanceId", ULONG),
        ("ParentInstanceId", ULONG),
        ("ParentGuid", _GUID),
        ("MofData", LPVOID),
        ("MofLength", ULONG),
        ("ClientContext", ULONG),
    ]

class SYSTEMTIME(ctypes.Structure):
    _fields_ = [
        ("wYear", WORD),
        ("wMonth", WORD),
        ("wDayOfWeek", WORD),
        ("wDay", WORD),
        ("wHour", WORD),
        ("wMinute", WORD),
        ("wSecond", WORD),
        ("wMilliseconds", WORD),
    ]

class TIME_ZONE_INFORMATION(ctypes.Structure):
    _fields_ = [
        ("Bias", LONG),
        ("StandardName", WCHAR * 32),
        ("StandardDate", SYSTEMTIME),
        ("StandardBias", LONG),
        ("DaylightName", WCHAR * 32),
        ("DaylightDate", SYSTEMTIME),
        ("DaylightBias", LONG),
    ]

class _TRACE_LOGFILE_HEADER(ctypes.Structure):
    _fields_ = [
        ("BufferSize", ULONG),
        ("Version", ULONG),
        ("ProviderVersion", ULONG),
        ("NumberOfProcessors", ULONG),
        ("EndTime", LARGE_INTEGER),
        ("TimerResolution", ULONG),
        ("MaximumFileSize", ULONG),
        ("LogFileMode", ULONG),
        ("BuffersWritten", ULONG),
        ("LogInstanceGuid", _GUID),
        ("LoggerName", LPWSTR),
        ("LogFileName", LPWSTR),
        ("TimeZone", TIME_ZONE_INFORMATION),
        ("BootTime", LARGE_INTEGER),
        ("PerfFreq", LARGE_INTEGER),
        ("StartTime", LARGE_INTEGER),
        ("ReservedFlags", ULONG),
        ("BuffersLost", ULONG),
    ]

class EVENT_DESCRIPTOR(ctypes.Structure):
    _fields_ = [
        ("Id", USHORT),
        ("Version", UCHAR),
        ("Channel", UCHAR),
        ("Level", UCHAR),
        ("Opcode", UCHAR),
        ("Task", USHORT),
        ("Keyword", ULARGE_INTEGER),
    ]

class EVENT_HEADER_EXTENDED_DATA_ITEM(ctypes.Structure):
    _fields_ = [
        ("Reserved1", USHORT),
        ("ExtType", USHORT),
        ("DataSize", USHORT),
        ("Reserved2", USHORT),
        ("DataPtr", LPVOID),
    ]

class EVENT_HEADER(ctypes.Structure):
    _fields_ = [
        ("Size", USHORT),
        ("HeaderType", USHORT),
        ("Flags", USHORT),
        ("EventProperty", USHORT),
        ("ThreadId", ULONG),
        ("ProcessId", ULONG),
        ("TimeStamp", LARGE_INTEGER),
        ("ProviderId", _GUID),
        ("EventDescriptor", EVENT_DESCRIPTOR),
        ("ProcessorTime", ULARGE_INTEGER),
        ("ActivityId", _GUID),
    ]

class EVENT_RECORD(ctypes.Structure):
    _fields_ = [
        ("EventHeader", EVENT_HEADER),
        ("BufferContext", ETW_BUFFER_CONTEXT),
        ("ExtendedDataCount", USHORT),
        ("UserDataLength", USHORT),
        ("ExtendedData", ctypes.POINTER(EVENT_HEADER_EXTENDED_DATA_ITEM)),
        ("UserData", LPVOID),
        ("UserContext", LPVOID),
    ]

PEVENT_RECORD_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.POINTER(EVENT_RECORD))
PEVENT_TRACE_BUFFER_CALLBACK = ctypes.WINFUNCTYPE(BOOL, ctypes.POINTER(EVENT_TRACE))

class _EVENT_TRACE_LOGFILEA(ctypes.Structure):
    _fields_ = [
        ("LogFileName", LPSTR),
        ("LoggerName", LPSTR),
        ("CurrentTime", LARGE_INTEGER),
        ("BuffersRead", ULONG),
        ("ProcessTraceMode", ULONG),
        ("CurrentEvent", EVENT_TRACE),
        ("LogfileHeader", _TRACE_LOGFILE_HEADER),
        ("BufferCallback", PEVENT_TRACE_BUFFER_CALLBACK),
        ("BufferSize", ULONG),
        ("Filled", ULONG),
        ("EventsLost", ULONG),
        ("EventRecordCallback", PEVENT_RECORD_CALLBACK),
        ("IsKernelTrace", ULONG),
        ("Context", LPVOID),
    ]

@PEVENT_RECORD_CALLBACK
def handle_trace_event(event_record):
    print(f"触发回调,事件ID: {event_record.contents.EventHeader.EventDescriptor.Id}")

OpenTraceA = advapi32.OpenTraceA
OpenTraceA.argtypes = [ctypes.POINTER(_EVENT_TRACE_LOGFILEA)]
OpenTraceA.restype = HANDLE

ProcessTrace = advapi32.ProcessTrace
ProcessTrace.argtypes = [ctypes.POINTER(HANDLE), ULONG, LPVOID, LPVOID]
ProcessTrace.restype = DWORD

def main():
    trace_logfile = _EVENT_TRACE_LOGFILEA()
    trace_logfile.LogFileName = b"sample.etl"
    trace_logfile.EventRecordCallback = handle_trace_event
    trace_logfile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD
    trace_logfile.BufferCallback = None  # 无需缓冲区回调

    trace_handle = OpenTrace
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:53:57