如何在Python ctypes中正确实现C库回调函数并获取True返回值?
ctypes中正确使用回调函数的方法
原代码存在的问题
- 回调函数类型与实际函数签名不匹配:定义的
USB_DLL_CALLBACK声明接收单个POINTER(Set_trigg)参数,但process函数写了两个参数,与C端回调签名不符。 - 未实际调用C库的
USBIO_SetTrigNotify函数:仅定义了回调和结构体,没有触发C库注册回调的核心逻辑。 - 回调函数未保留引用:局部变量
cl_back会在函数结束后被Python垃圾回收,导致C端调用时出现内存错误。 - 回调函数未返回指定类型值:C库要求回调返回
c_bool类型,但process函数无返回值,无法返回True。
修正后的完整代码
from ctypes import * class Set_trigg(Structure): _fields_ = [ ('dev_index', c_byte), ('type', c_ulong) ] class USB2ish(object): def __init__(self): try: # 保存DLL实例,避免被Python垃圾回收 self.usbio_dll = cdll.LoadLibrary("C:\\Users\\...\\usb2uis.dll") # 显式声明C函数的参数和返回值类型,确保类型匹配 self.usbio_dll.USBIO_OpenDevice.restype = c_int self.usbio_dll.USBIO_SetTrigNotify.argtypes = [c_byte, CFUNCTYPE(c_bool, POINTER(Set_trigg))] self.usbio_dll.USBIO_SetTrigNotify.restype = c_bool except Exception as e: print(f"DLL加载失败:{e},请确认USB-TO-UIS驱动已安装") # 保存回调实例引用,防止被垃圾回收 self.callback = None def exception_handler(self, exception): print(f"发生异常:{exception}") def USBIO_OpenDevice(self): try: result = self.usbio_dll.USBIO_OpenDevice() print(f"设备打开成功,设备编号:{result}") return result except Exception as ex: self.exception_handler(ex) return -1 def USBIO_SetTrigNotify(self, dev_no): """注册触发事件回调,设备触发时会自动调用process函数""" # 定义回调函数类型:返回c_bool,参数为Set_trigg结构体指针 USB_DLL_CALLBACK = CFUNCTYPE(c_bool, POINTER(Set_trigg)) # 绑定回调函数并保存为类成员,避免被回收 self.callback = USB_DLL_CALLBACK(self.process) # 调用C库的回调注册函数,传递设备号和回调指针 ret = self.usbio_dll.USBIO_SetTrigNotify(dev_no, self.callback) print(f"回调注册结果:{'成功' if ret else '失败'}") return ret def process(self, trig_info): """回调函数实现,参数为C端传递的Set_trigg结构体指针""" print(f"触发设备索引:{trig_info.contents.dev_index}") print(f"触发类型:{hex(trig_info.contents.type)}") # 返回True给C库,满足回调返回值要求 return True if __name__ == "__main__": rs = USB2ish() dev_result = rs.USBIO_OpenDevice() if dev_result != -1: # 注册回调,传入目标设备编号 register_ok = rs.USBIO_SetTrigNotify(0)
关键注意事项
- 回调签名严格匹配:
CFUNCTYPE的参数和返回值必须与C库的回调原型完全一致。假设C端回调原型为bool (*USB_DLL_CALLBACK)(Set_trigg*),Python回调只需接收一个POINTER(Set_trigg)参数。 - 保留回调引用:必须将回调实例保存为类成员或全局变量,否则Python垃圾回收器会在函数结束后释放回调内存,导致C端调用时崩溃。
- 显式声明函数类型:通过
argtypes和restype声明C函数的参数和返回值类型,ctypes会自动进行类型校验,避免因类型不匹配引发的隐性错误。 - 返回指定类型值:回调函数必须返回
c_bool类型的True或False,确保符合C库的返回值要求,这样就能正确返回True。 - 调用C库注册函数:必须调用C库提供的
USBIO_SetTrigNotify函数完成回调注册,否则C端不会触发回调逻辑。
内容的提问来源于stack exchange,提问作者SABARI SARAVANAN M
相关产品推荐
相关产品推荐

