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

Python通过ctypes加载C DLL时指针及回调函数取值异常问题求助

问题修正方案

核心错误点整理

  • 结构体定义不符合C原生规则:
    1. 头文件中unsigned int对应ctypes的c_uint而非c_int,类型不匹配会导致内存解析错误
    2. 数组类型定义错误,float distance[256]的正确ctypes写法是('distance', c.c_float * 256),错误的字段名会导致内存偏移错位
  • 回调函数使用错误:
    1. 注册回调时直接调用了回调实例并传参,正确做法是直接传递回调对象本身,不需要额外加参数
    2. device_add的argtypes漏写了第三个回调参数的类型,导致ctypes传参栈不平衡
  • API参数类型定义错误:
    1. DeviceHandler本身是无符号整型值,不是指针,device_get_info的第一个参数类型错设为指针类型,导致API接收不到正确的设备句柄
    2. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 14:45:03