Python通过ctypes加载C DLL时指针及回调函数取值异常问题求助
问题修正方案
核心错误点整理
- 结构体定义不符合C原生规则:
- 头文件中
unsigned int对应ctypes的c_uint而非c_int,类型不匹配会导致内存解析错误 - 数组类型定义错误,
float distance[256]的正确ctypes写法是('distance', c.c_float * 256),错误的字段名会导致内存偏移错位
- 头文件中
- 回调函数使用错误:
- 注册回调时直接调用了回调实例并传参,正确做法是直接传递回调对象本身,不需要额外加参数
device_add的argtypes漏写了第三个回调参数的类型,导致ctypes传参栈不平衡
- API参数类型定义错误:
DeviceHandler本身是无符号整型值,不是指针,device_get_info的第一个参数类型错设为指针类型,导致API接收不到正确的设备句柄device_get_info返回值为void,不需要设为c_void_p
修正后完整代码
import ctypes as c # 结构体定义修正 class MeasureResult_t(c.Structure): _fields_ = [ ('check_id', c.c_uint), # 对应unsigned int ('distance', c.c_float * 256) # 数组正确定义方式 ] class DeviceInfo_t(c.Structure): _fields_ = [ ('fpga_version', c.c_uint) # 对应unsigned int ] # 回调函数定义:注意返回值和参数要和C原型一致 CMPFUNC_t = c.CFUNCTYPE(None, c.c_void_p, c.c_uint) def device_ID1_callback(out, out_num): print("---enter device call back function---") # 把void*转为MeasureResult_t指针 result_ptr = c.cast(out, c.POINTER(MeasureResult_t)) print(f"check_id: 0x{result_ptr[0].check_id:x}") print(f"out_num: {out_num}") # 示例取第一个distance值 print(f"distance[0]: {result_ptr[0].distance[0]}") print("---exit device call back function---\n\n") # 加载DLL _dev = c.CDLL("./OPSensor/osp_lidar") # 类型定义修正:DeviceHandler是unsigned int T_device_handler = c.c_uint # 配置device_add参数类型 _dev.device_add.argtypes = [ c.POINTER(T_device_handler), # outHandler是输出指针 c.c_char_p, # device_ip CMPFUNC_t # 回调函数类型,之前漏了 ] _dev.device_add.restype = c.c_int # 配置device_get_info参数类型 _dev.device_get_info.argtypes = [ T_device_handler, # 第一个参数是设备句柄值,不是指针! c.POINTER(DeviceInfo_t) ] _dev.device_get_info.restype = None # 返回值为void # 业务逻辑 # 1. 初始化回调实例,注意要把这个实例变量存到全局,不要被GC回收 callback_instance = CMPFUNC_t(device_ID1_callback) device_handler = T_device_handler() ip_val = b"192.168.1.2" # 直接传字节串即可 # 2. 调用device_add,第三个参数直接传回调实例,不要加括号传参 ret = _dev.device_add(c.byref(device_handler), ip_val, callback_instance) print(f"device_add ret: {ret}") # 3. 调用device_get_info device_info = DeviceInfo_t() _dev.device_get_info(device_handler, c.byref(device_info)) print(f"device_handler: {device_handler.value}") print(f"FPGA version: {device_info.fpga_version}")
额外注意点
如果DLL的调用约定是stdcall(比如EXCALL是__stdcall的宏定义),需要把CDLL换成WinDLL,CFUNCTYPE换成WINFUNCTYPE。
内容的提问来源于stack exchange,提问作者Bill W
相关产品推荐
相关产品推荐

