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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:32:04