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

Python调用SetupDiEnumDeviceInterfaces报WinError 6(无效句柄)求助

问题:SetupDiEnumDeviceInterfaces返回无效句柄错误(WinError 6)

我正在将一段可正常运行的Tcl代码转换为Python代码,已确认GUID十六进制值正确,但调用winapi.SetupDiEnumDeviceInterfaces时返回0,触发WinError错误码6,报错信息如下:

OSError: [WinError 6] The handle is invalid.

相关代码:

from ctypes import *
from ctypes import wintypes
import win32file
 
DIGCF_DEVICEINTERFACE          = 0x00000010
DIGCF_PRESENT                  = 0x00000002
FILE_FLAG_OVERLAPPED           = 0x40000000
FILE_SHARE_READ                = 0x00000001
FILE_SHARE_WRITE               = 0x00000002
FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
FORMAT_MESSAGE_FROM_SYSTEM     = 0x00001000
GENERIC_READ                   = 0x80000000
GENERIC_WRITE                  = 0x40000000
OPEN_EXISTING                  = 3
WAIT_TIMEOUT                   = 0x00000102
INVALID_HANDLE_VALUE           = -1
 
winapi = windll.setupapi
 
 
class GUID(Structure):
    _fields_ = [
        ('Data1', c_ulong),
        ('Data2', c_ushort),
        ('Data3', c_ushort),
        ('Data4', c_ubyte*8),
    ]
    def __str__(self):
        return "{%08x-%04x-%04x-%s-%s}" % (
            self.Data1,
            self.Data2,
            self.Data3,
            ''.join(["%02x" % d for d in self.Data4[:2]]),
            ''.join(["%02x" % d for d in self.Data4[2:]]),
        )
 
 
class SP_DEVICE_INTERFACE_DATA(Structure):
    _fields_ = [
        ('cbSize', wintypes.DWORD),
        ('InterfaceClassGuid', GUID),
        ('Flags', wintypes.DWORD),
        ('Reserved', POINTER(c_ulong)),
    ]
    def __str__(self):
        self.cbSize = sizeof(self)
        return "InterfaceClassGuid:%s Flags:%s" % (self.InterfaceClassGuid, self.Flags)
 
 
GUID_SPWR_BASE_INTERFACE = GUID(0xa480c318, 0xfd41, 0x4973,
    (c_ubyte*8)(0xa5,0x4a, 0xa8, 0x24, 0x93, 0x11, 0xc7, 0x71))
GUID_SPWR_CHAN_INTERFACE = GUID(0xe1eda58b, 0xf16c, 0x4489, (c_ubyte * 8)(0xa4,0xe9, 0xb4, 0xaa, 0x4f, 0x12, 0xb1, 0x6b))
 
 
hDevInfo = winapi.SetupDiGetClassDevsA(byref(GUID_SPWR_BASE_INTERFACE), None, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)
 
print(hDevInfo)
 
for index in range(256):
    interfaceData = SP_DEVICE_INTERFACE_DATA()
    if not winapi.SetupDiEnumDeviceInterfaces(hDevInfo, None, byref(GUID_SPWR_BASE_INTERFACE), index, byref(interfaceData)):
        if GetLastError() != 0:
            raise WinError()
错误原因分析
  • SP_DEVICE_INTERFACE_DATA的cbSize未正确初始化:
    代码中仅在__str__方法中设置cbSize,但循环创建interfaceData后并未调用__str__,导致该字段为默认0值。Windows API要求此字段必须设为结构体的实际大小,否则会导致接口调用失败,进而触发无效句柄错误。

  • 未校验SetupDiGetClassDevsA的返回值:
    SetupDiGetClassDevsA可能因权限不足、GUID不匹配或设备不存在等原因返回INVALID_HANDLE_VALUE(即-1)。直接使用无效句柄调用SetupDiEnumDeviceInterfaces,必然触发错误码6的无效句柄报错。

  • 错误码获取逻辑存在隐患:
    在ctypes中,需在API调用后立即用GetLastError()获取错误码,避免被其他操作覆盖。另外,当SetupDiEnumDeviceInterfaces返回False时,错误码为0通常表示枚举完成,但前提是句柄本身有效——若句柄已失效,错误码会直接反映句柄问题。

修复建议
  • 初始化SP_DEVICE_INTERFACE_DATA的cbSize:
    可在类的构造方法中自动设置,或在创建实例后手动赋值:

    # 方法1:修改类定义添加构造方法
    class SP_DEVICE_INTERFACE_DATA(Structure):
        _fields_ = [
            ('cbSize', wintypes.DWORD),
            ('InterfaceClassGuid', GUID),
            ('Flags', wintypes.DWORD),
            ('Reserved', POINTER(c_ulong)),
        ]
        def __init__(self):
            super().__init__()
            self.cbSize = sizeof(self)
        def __str__(self):
            return "InterfaceClassGuid:%s Flags:%s" % (self.InterfaceClassGuid, self.Flags)
    
    # 方法2:创建实例后手动设置
    interfaceData = SP_DEVICE_INTERFACE_DATA()
    interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA)
    
  • 校验SetupDiGetClassDevsA的返回值:
    获取句柄后立即检查有效性:

    hDevInfo = winapi.SetupDiGetClassDevsA(byref(GUID_SPWR_BASE_INTERFACE), None, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)
    if hDevInfo == INVALID_HANDLE_VALUE:
        raise WinError()
    
  • 优化错误码处理逻辑:
    确保API调用后立即获取错误码,并正确处理枚举完成的情况:

    if not winapi.SetupDiEnumDeviceInterfaces(hDevInfo, None, byref(GUID_SPWR_BASE_INTERFACE), index, byref(interfaceData)):
        err_code = GetLastError()
        if err_code != 0:
            raise WinError(err_code)
        else:
            break  # 枚举完成,退出循环
    

内容的提问来源于stack exchange,提问作者Kelble

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:45:56