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

