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

调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 05:31:36