【问题标题】:Loading c DLL with python, problems with pointer用python加载c DLL,指针问题
【发布时间】:2022-01-04 05:04:19
【问题描述】:

目前我有 C++ 加载 DLL。我需要用 python 替换 C++ 代码。我的问题是:

  1. 在回调函数 device_ID1_callback 中,所有值似乎都是空的,我猜我没有正确使用指针。
  2. 在调用 device_get_info 之后,所有的值都是 0,我想得到一些不为零的值。 几个星期以来,我尝试了任何我能想到的方法,但运气不佳。

为了简化问题,这里是我的部分代码。感谢您的时间和帮助!

在我的 lib.h 文件中,我有

typedef unsigned int DeviceHandler;
typedef struct {
        unsigned int fpga_version;
}DeviceInfo_t;
typedef struct {
        unsigned int check_id;
        float distance[256];
}MeasureResult_t;
DLLEPXORT int EXCALL device_add(DeviceHandler* outHandler, char* device_ip, MeasureModeCallback callback);
DLLEPXORT void EXCALL device_get_info(DeviceHandler handler, DeviceInfo_t* p_device_info);

在示例 C++ 文件中: """

void device_ID1_callback(const void *out,unsigned int out_num){
    MeasureResult_t *ptr = (MeasureResult_t *)out;
    printf("[ChechID:0x%x] %d pack's data\n",ptr[0].check_id,out_num);
}
void demo_callback_mode(){
    int ret;
    DeviceHandler device_handler;
    DeviceInfo_t device_info;

    ret = device_add(&device_handler,"192.168.1.2",&device_ID1_callback);
    device_get_info(device_handler,&device_info);

    printf("[FPGA] version : %d\n", device_info.fpga_version);
}

""" *c++ 结束 *

这是我的 Python 代码: """

import ctypes   as c

class MeasureResult_t(c.Structure):
    _fields_  = [
            ('check_id', c.c_int),
            ('distance[256]', c.c_float)]
    
class DeviceInfo_t(c.Structure):
    _fields_  = [
            ('fpga_version', c.c_int)
            ]
    
def device_ID1_callback(out, out_num):
    print("---enter device call back function---")
    print(dir(out))
    print("out: ",out.contents)
    #print(dir(out))
    print("out_num:",out_num)
    print("---exit device call back function---\n\n")
    return 0

_dev = c.CDLL("./OPSensor/osp_lidar")

T_device_handler = c.c_int
T_device_handler_ptr = c.POINTER(T_device_handler)

_dev.device_add.argtypes = [T_device_handler_ptr, c.c_char_p]
_dev.device_add.restype = c.c_int


device_handler = c.c_int()
ip_val = c.c_char_p("192.168.1.2".encode('utf-8'))

    
out = MeasureResult_t()
out_num = c.c_int()


CMPFUNC_t = c.CFUNCTYPE(None, c.POINTER(MeasureResult_t), c.c_int)
MeasureModeCallback = CMPFUNC_t(device_ID1_callback)

                   
ret = _dev.device_add(c.byref(device_handler), (ip_val), MeasureModeCallback(c.byref(out), out_num))

    
_dev.device_get_info.argtypes = [T_device_handler_ptr, c.POINTER(DeviceInfo_t)]
_dev.device_get_info.restype = c.c_void_p # assume it returns C int

p_device_info = DeviceInfo_t()
#_dev.device_get_info(c.byref(device_handler), c.byref(p_device_info)) # does not work
_dev.device_get_info((device_handler), c.byref(p_device_info)) #does not work either

print(device_handler) # I have correct device_handler value 
print(p_device_info.fpga_version) # the value i got is 0, does seem right

"""

【问题讨论】:

  • 关于 1. 您在代码中调用“MeasureModeCallback”,而您只能将其用作“device_add”的参数。
  • 关于 2. 在 C++ 代码中调用“device_get_info”,其值为“device_handler”作为第一个参数,但在 Python 中使用指向“device_handler”(“byref”)的指针。
  • 感谢您花时间看我的帖子。我可能是错的,但 MeasureModeCallback 是一个带有返回值和两个输入变量的 CFUNCTYPE 变量。 CMPFUNC_t = c.CFUNCTYPE(None, c.POINTER(MeasureResult_t), c.c_int) MeasureModeCallback = CMPFUNC_t(device_ID1_callback) 关于 2) 我尝试了很多不同的东西,这只是我尝试过的一个版本。它们都不适合我。
  • “MeasureModeCallback”是具有指定参数和输出类型的特定函数类型“CMPFUNC_t”的实例。这种函数类型的实例是一个函数,调用这个函数会产生一个返回值。
  • 迈克尔,很明显我不知道我在说什么。你完全正确。再次感谢您的帮助。

标签: python c++ dll callback ctypes


【解决方案1】:

这是我对minimal reproducible example 的尝试。我实现了演示您描述的回调的虚拟函数:

// lib.c
#define DLLEXPORT __declspec(dllexport)
#define EXCALL

typedef unsigned int DeviceHandler;

typedef struct {
        unsigned int fpga_version;
} DeviceInfo_t;

typedef struct {
        unsigned int check_id;
        float distance[256];
} MeasureResult_t;

typedef void (*MeasureModeCallback)(MeasureResult_t*, unsigned int);

DLLEXPORT int EXCALL device_add(DeviceHandler* outHandler, char* device_ip, MeasureModeCallback callback) {
    *outHandler = 123;  // dummy device ID
    MeasureResult_t m;  // some fake measurement results
    m.check_id = 456;
    for(int i = 0; i < 256; ++i)
        m.distance[i] = (float)(i * .25);
    callback(&m, 789);  // call the callback
    return 1;
}

DLLEXPORT void EXCALL device_get_info(DeviceHandler handler, DeviceInfo_t* p_device_info) {
    p_device_info->fpga_version = handler * 2; // fake fpga version
}

要在 Python 中创建回调,请将 CFUNCTYPE 原型分配给回调类型,用该类型装饰回调函数,并在回调参数定义中使用该类型,并在将其作为参数传递时使用实际函数名称.

还要注意 float distance[256] 在 Python 中声明为 c.c_float * 256 以创建数组以及函数的 .argtypes/.restype 属性的差异。例如,device_get_info 采用 DeviceHandler,而不是 c.POINTER(DeviceHandler)

# test.py
import ctypes as c

class MeasureResult_t(c.Structure):
    _fields_  = (('check_id', c.c_uint),
                 ('distance', c.c_float * 256))
    def __repr__(self):  # defines how to display this class
        return f'MeasureResult_t(check_id={self.check_id}, distance=[{self.distance[0]}, ..., {self.distance[255]}])'

class DeviceInfo_t(c.Structure):
    _fields_  = ('fpga_version', c.c_uint),
    def __repr__(self):  # defines how to display this class
        return f'DeviceInfo_t(fpga_version={self.fpga_version})'

# Declare the callback type
MeasureModeCallback = c.CFUNCTYPE(None, c.POINTER(MeasureResult_t), c.c_uint)

DeviceHandler = c.c_uint

# apply the decorator so this function can be called from C
@MeasureModeCallback
def device_ID1_callback(out, out_num):
    print('---enter device call back function---')
    print('out: ',out.contents)
    print('out_num:',out_num)
    print('---exit device call back function---')

_dev = c.CDLL('./lib')
# Use the argument type
_dev.device_add.argtypes = c.POINTER(DeviceHandler), c.c_char_p, MeasureModeCallback
_dev.device_add.restype = c.c_int
_dev.device_get_info.argtypes = DeviceHandler, c.POINTER(DeviceInfo_t)
_dev.device_get_info.restype = None

device_handler = DeviceHandler()
ip_val = b'192.168.1.2'

# Use the callback function name when calling the function
ret = _dev.device_add(c.byref(device_handler), ip_val, device_ID1_callback)

device_info = DeviceInfo_t()
_dev.device_get_info(device_handler, c.byref(device_info))

print(f'{device_handler.value=}')
print(f'{device_info=}')

输出。请注意,这些类知道如何显示自己,并且假数据与我的实现一致:

---enter device call back function---
out:  MeasureResult_t(check_id=456, distance=[0.0, ..., 63.75])
out_num: 789
---exit device call back function---
device_handler.value=123
device_info=DeviceInfo_t(fpga_version=246)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-15
    • 2011-08-02
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多