调用SetupDiGetDeviceRegistryPropertyW获取USB电源数据始终返回8
问题描述
使用SetupDiGetDeviceRegistryPropertyW获取USB设备电源数据时,接口调用始终返回错误码8,无法获取有效数据。
错误原因
代码存在5处直接导致调用失败的问题:
- 仅定义了ANSI版本
SetupDiGetDeviceRegistryPropertyA的参数、返回值类型约束,实际调用的宽字符版本SetupDiGetDeviceRegistryPropertyW未做类型声明,ctypes默认传参规则和API调用约定不匹配,入栈参数错误触发调用失败。 SetupDiGetClassDevsW第一个参数要求传入GUID指针,代码直接传入结构体实例,参数类型不匹配。- 用于接收属性类型的
PropertyRegDataType参数传入None,该参数为API要求的有效输出参数,传空指针会直接触发参数校验失败。 - 读取到属性数据后错误将二进制缓冲区强转为宽字符指针解析,
SPDRP_DEVICE_POWER_DATA返回的是CM_POWER_DATA结构体二进制数据,并非字符串,强转无法得到正确结果。 - 枚举逻辑存在缺陷:单台设备读取属性失败就直接
break终止整个枚举流程,会漏过后续所有设备。
修正后可运行代码
import ctypes as ct from ctypes import wintypes as w import uuid SetupAPI = ct.WinDLL('SetupAPI') ULONG_PTR = w.WPARAM class HDEVINFO(w.HANDLE): pass class GUID(ct.Structure): _fields_ = (('Data1', ct.c_ulong), ('Data2', ct.c_ushort), ('Data3', ct.c_ushort), ('Data4', ct.c_ubyte * 8)) def __repr__(self): return f"GUID('{self}')" def __str__(self): return (f'{{{self.Data1:08x}-{self.Data2:04x}-{self.Data3:04x}-' f'{bytes(self.Data4[:2]).hex()}-{bytes(self.Data4[2:]).hex()}}}') def __init__(self,guid=None): if guid is not None: data = uuid.UUID(guid) self.Data1 = data.time_low self.Data2 = data.time_mid self.Data3 = data.time_hi_version self.Data4[0] = data.clock_seq_hi_variant self.Data4[1] = data.clock_seq_low self.Data4[2:] = data.node.to_bytes(6,'big') PGUID = ct.POINTER(GUID) GUID_DEVINTERFACE_USB_DEVICE = GUID('{A5DCBF10-6530-11D2-901F-00C04FB951ED}') class SP_DEVINFO_DATA(ct.Structure): _fields_ = (('cbSize', w.DWORD), ('ClassGuid', GUID), ('DevInst', w.DWORD), ('Reserved', ULONG_PTR)) def __repr__(self): return f'SP_DEVINFO_DATA(ClassGuid={self.ClassGuid}, DevInst={self.DevInst})' def __init__(self): self.cbSize = ct.sizeof(SP_DEVINFO_DATA) PSP_DEVINFO_DATA = ct.POINTER(SP_DEVINFO_DATA) ENUM = ct.c_int POWER_SYSTEM_MAXIMUM = 7 class _SYSTEM_POWER_STATE(ENUM): PowerSystemUnspecified = 0 PowerSystemWorking = 1 PowerSystemSleeping1 = 2 PowerSystemSleeping2 = 3 PowerSystemSleeping3 = 4 PowerSystemHibernate = 5 PowerSystemShutdown = 6 PowerSystemMaximum = 7 SYSTEM_POWER_STATE = _SYSTEM_POWER_STATE class _DEVICE_POWER_STATE(ENUM): PowerDeviceUnspecified = 0 PowerDeviceD0 = 1 PowerDeviceD1 = 2 PowerDeviceD2 = 3 PowerDeviceD3 = 4 PowerDeviceMaximum = 5 DEVICE_POWER_STATE = _DEVICE_POWER_STATE PDCAP_D0_SUPPORTED = 0x00000001 PDCAP_D1_SUPPORTED = 0x00000002 PDCAP_D2_SUPPORTED = 0x00000004 PDCAP_D3_SUPPORTED = 0x00000008 PDCAP_WAKE_FROM_D0_SUPPORTED = 0x00000010 PDCAP_WAKE_FROM_D1_SUPPORTED = 0x00000020 PDCAP_WAKE_FROM_D2_SUPPORTED = 0x00000040 PDCAP_WAKE_FROM_D3_SUPPORTED = 0x00000080 PDCAP_WARM_EJECT_SUPPORTED = 0x00000100 class CM_Power_Data_s(ct.Structure): _fields_ = [ ('PD_Size', w.ULONG), ('PD_MostRecentPowerState', DEVICE_POWER_STATE), ('PD_Capabilities', w.ULONG), ('PD_D1Latency', w.ULONG), ('PD_D2Latency', w.ULONG), ('PD_D3Latency', w.ULONG), ('PD_PowerStateMapping', DEVICE_POWER_STATE * POWER_SYSTEM_MAXIMUM), ('PD_DeepestSystemWake', SYSTEM_POWER_STATE), ] PCM_POWER_DATA = ct.POINTER(CM_Power_Data_s) # 补全所有调用API的参数、返回值类型约束,W版本必须单独声明 SetupAPI.SetupDiGetClassDevsW.argtypes = PGUID, w.PWCHAR, w.HWND, w.DWORD SetupAPI.SetupDiGetClassDevsW.restype = HDEVINFO SetupAPI.SetupDiEnumDeviceInfo.argtypes = HDEVINFO, w.DWORD, PSP_DEVINFO_DATA SetupAPI.SetupDiEnumDeviceInfo.restype = w.BOOL SetupAPI.SetupDiDestroyDeviceInfoList.argtypes = (HDEVINFO,) SetupAPI.SetupDiDestroyDeviceInfoList.restype = w.BOOL SetupAPI.SetupDiGetDeviceRegistryPropertyW.argtypes = HDEVINFO, PSP_DEVINFO_DATA, w.DWORD, w.PDWORD, w.PBYTE, w.DWORD, w.PDWORD SetupAPI.SetupDiGetDeviceRegistryPropertyW.restype = w.BOOL DIGCF_PRESENT = 0x00000002 DIGCF_DEVICEINTERFACE = 0x00000010 SPDRP_DEVICE_POWER_DATA = 0x0000001E if __name__ == '__main__': Flags = DIGCF_DEVICEINTERFACE | DIGCF_PRESENT # GUID参数必须传指针 hDevInfo = SetupAPI.SetupDiGetClassDevsW(ct.byref(GUID_DEVINTERFACE_USB_DEVICE), None, None, Flags) if not hDevInfo: raise ct.WinError() try: MemberIndex = 0 while True: devinfo = SP_DEVINFO_DATA() if not SetupAPI.SetupDiEnumDeviceInfo(hDevInfo, MemberIndex, ct.byref(devinfo)): break print(f"\n=== 设备索引 {MemberIndex}: {devinfo} ===") RequiredSize = w.DWORD() PropertyRegDataType = w.DWORD() # 第一次调用获取所需缓冲区大小,必须传入有效类型接收指针 ret = SetupAPI.SetupDiGetDeviceRegistryPropertyW( hDevInfo, ct.byref(devinfo), SPDRP_DEVICE_POWER_DATA, ct.byref(PropertyRegDataType), None, 0, ct.byref(RequiredSize) ) err = ct.GetLastError() # 传空缓冲区正常返回错误码122(ERROR_INSUFFICIENT_BUFFER)同时输出需要的大小 if err != 122 or RequiredSize.value == 0: print(f"该设备不支持电源数据属性,错误码: {err}") MemberIndex +=1 continue PropertyBuffer = (w.BYTE * RequiredSize.value)() ret = SetupAPI.SetupDiGetDeviceRegistryPropertyW( hDevInfo, ct.byref(devinfo), SPDRP_DEVICE_POWER_DATA, ct.byref(PropertyRegDataType), PropertyBuffer, ct.sizeof(PropertyBuffer), ct.byref(RequiredSize) ) if not ret: print(f"读取电源数据失败,错误码: {ct.GetLastError()}") MemberIndex +=1 continue # 直接将缓冲区转为对应结构体指针解析 power_data = ct.cast(PropertyBuffer, PCM_POWER_DATA).contents print(f"当前电源状态: {power_data.PD_MostRecentPowerState.value}") print(f"电源能力标志: {hex(power_data.PD_Capabilities)}") print(f"D1状态唤醒延迟: {power_data.PD_D1Latency}ms") print(f"D2状态唤醒延迟: {power_data.PD_D2Latency}ms") print(f"D3状态唤醒延迟: {power_data.PD_D3Latency}ms") MemberIndex += 1 finally: SetupAPI.SetupDiDestroyDeviceInfoList(hDevInfo)
注意事项
- 用ctypes调用Windows API时,必须严格匹配实际调用的函数版本(ANSI/宽字符)的参数类型、返回值类型,不能只声明单版本约束混用另一版本。
- API标注为输出参数的位置,必须传入对应类型的有效指针,禁止传
None,否则会直接触发参数校验失败。 - 读取SPDRP属性前要确认属性对应的数据格式,结构体类型的属性不能按字符串方式解析。
- 设备枚举逻辑中,单设备读取失败要跳过当前设备继续枚举,不能直接终止整个循环。
内容的提问来源于stack exchange,提问作者user19212551
相关产品推荐
相关产品推荐

