python3cTypes调用的函数需要通过另一个结构间接引用缓冲区

2024-10-03 13:28:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个C共享库,它的函数只有一个参数。 此参数是指向具有两个字段的结构的指针

    typedef struct
    {   
        uint8_t     *p_data; // Pointer to a fixed lenth buffer (100 bytes)
        uint16_t     len;    // number of valid bytes in the buffer (range 1-100)
    } data_t;

我需要在python3脚本中设置一个100字节的缓冲区(我使用的是3.7.2/3.7.3), 加载库并调用此函数

    int 
    fn_convert_buffer(data_t *data_p)
    {
        ...
    }

我的python3ctypes调用尝试命中不兼容的类型

    import ctypes as ct
    # load the library, etc...
    # lib_cdll = ct.CDLL(mySharedLib)

    def c_py_fn_convert_buffer(b_p):
            global lib_cdll
            val = lib_cdll.fn_convert_buffer(ct.byref(b_p))
            return int(val)

    data_a = bytearray(100)
    # Initialize the buffer with data.

    uint8_p = ct.c_uint8 * len(data_a)
    class BufferStruct_t (ct.Structure):
            _pack_ = 1
            _fields_ = [
                    ("p_data", ct.POINTER(ct.c_uint8 * len(data_a))),
                    ("len",    ct.c_uint16)
                    ]

    data_buf = BufferStruct_t(uint8_p.from_buffer(data_a), ct.c_uint16(8))
    # TypeError: incompatible types, c_ubyte_Array_100 instance 
    #            instead of LP_c_ubyte_Array_100 instance

    # Call C function in shared-library: int fn_convert_buffer(data_t *data_p);
    z = c_py_fn_convert_buffer(data_buf)

我需要帮助来理解我在上面的BufferStruct_t定义中遗漏了什么。from_buffer应该得到一个指向缓冲区的指针,但它似乎得到了c_ubyte_ARRAY_100

一个byref()也不起作用

    data_buf = BufferStruct_t(ct.byref(uint8_p.from_buffer(data_a)), ct.c_uint16(8))
    # TypeError: expected LP_c_ubyte_Array_100 instance, got CArgObject

为了测试我的流的基础知识,我做了一个示例,分别发送缓冲区和长度参数

    def c_py_fn_convert_data(d_p,l):
            global lib_cdll
            val = lib_cdll.fn_convert_data(ct.byref(d_p),ct.c_uint32(l))
            return int(val)

    test_a = ct.c_uint8 * len(data_a)
    # Call C function in shared-library: 
    #   int fn_convert_data(uint8_t *data_p, uint32_t length); 
    z = c_py_fn_convert_data(test_a.from_buffer(data_a), 8)

这个简化的案例有效

如何构建一个python3对象,该对象包含对共享库函数所期望的缓冲区的引用


更新两个有效的变体。
更新1根据我稍后阅读的内容尝试了cast(我不会轻易地:-)
改变了

    data_buf = BufferStruct_t(uint8_p.from_buffer(data_a), ct.c_uint16(8))

转换为引用特定长度数组的指针

    data_buf = BufferStruct_t(cast(uint8_p.from_buffer(data_a),
                                   ct.POINTER(ct.c_uint8 * len(data_a))),
                              ct.c_uint16(8))

根据马克的答案更新2。 更改_field_

                    ("p_data", ct.POINTER(ct.c_uint8 * len(data_a))),

一个简单的指针形式

                    ("p_data", ct.POINTER(ct.c_uint8)),

两种方法都有效。
不过,我很想知道这两种方法中哪一种更安全/正确地处理ctypes。

  1. 以阵型施展是否更好?或者
  2. 使用简单的指针和独立发送的长度是否更好

Tags: fromconvertdatalenlibbufferintfn
1条回答
网友
1楼 · 发布于 2024-10-03 13:28:46

您的结构定义声明了指向数组的指针,而不是C结构中的简单指针。下面是一个简单的DLL实现的工作示例,其中函数对数据求和:

测试.c

#include <stdint.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
#   define API
#endif

typedef struct {
    uint8_t     *p_data;
    uint16_t     len;
} data_t;

API int fn_convert_buffer(data_t *data_p)
{
    int i;
    int sum = 0;
    for(i = 0; i < data_p->len; ++i)
        sum += data_p->p_data[i];
    return sum;
}

测试.py

import ctypes as ct

class BufferStruct_t(ct.Structure):

    _pack_ = 1
    _fields_ = [("p_data", ct.POINTER(ct.c_uint8)), # just a pointer                    
                ("len",    ct.c_uint16)]

    # Helper to initialize the data
    def __init__(self,data):
        self.p_data = (ct.c_uint8 * len(data))(*data)
        self.len = len(data)

dll = ct.CDLL('test')
dll.fn_convert_buffer.argtypes = ct.POINTER(BufferStruct_t),
dll.fn_convert_buffer.restype = ct.c_int

data_buf = BufferStruct_t([1,2,3,4,5])
print(dll.fn_convert_buffer(data_buf))

输出:

15

相关问题 更多 >