【问题标题】:Python ctypes Allocate Memory for StructPython ctypes 为结构分配内存
【发布时间】:2021-08-12 01:42:26
【问题描述】:

我正在尝试通过我自己的 Python 代码使用 sensorSenis,方法是将 ctypes 与公司提供的 dll 一起使用。

manual 描述获取传感器测量值的函数声明如下:

typedef struct {
int dimSize;
int elt[x];
} TD2;
typedef TD2 **TD2Hdl;

int get_sensor_values (int* device_number, unsigned long* timestamp, TD2Hdl values )

我已尝试将此 dll 声明与我自己的代码一起使用:

from ctypes import *

class TD2Hdl(Structure):
    _fields_ = [("dimSize", c_int),
                ("elt", POINTER(c_int))]
    
if __name__ == '__main__':
    
    senDLL = cdll.LoadLibrary("3mtslib.dll")
    
    num = c_int(0)
    ts = c_ulong(0)
    td = TD2Hdl(0, None)

    senDLL.open_device(byref(c_int(0)))
    senDLL.get_sensor_values(byref(c_int(num)), byref(ts), td)

我还尝试通过以下实例为“elt”变量分配不同大小的 int 数组

td = TD2Hdl(0, (c_int*8)(*arr))

具有不同的数组大小。但是,无论我尝试什么,都会读取到访问冲突。

在理解我在如何为传递给 ctypes 函数的结构分配内存方面所缺少的任何帮助方面,我将不胜感激。

我用 dumpbin 验证了函数名,明确地说,其余函数运行良好。

谢谢!

【问题讨论】:

    标签: ctypes


    【解决方案1】:

    对于TD2Hdl,这是一个奇怪的 typedef,但假设它是由于 C 代码返回分配的结构,下面演示了工作的 Python 代码。为了演示目的,我制作了一个虚拟 DLL 来分配和填充结构。

    test.c

    #include <stdlib.h>
    
    #ifdef _WIN32
    #   define API __declspec(dllexport)
    #else
    #   define API
    #endif
    
    typedef struct {
    int dimSize;
    int elt[1];  // 'x' wasn't legal, so assume size 1 and allocate more memory as needed.
    } TD2;
    typedef TD2 **TD2Hdl;
    
    API void open_device(int* device_number) {
        *device_number = 5;
    }
    
    API int get_sensor_values(int* device_number, unsigned long* timestamp, TD2Hdl values) {
        int dimSize = 10;     // could be any size
        *device_number += 1;  // just to prove the pointer content can change
        *timestamp = 123;
        *values = malloc(sizeof(TD2) + (dimSize - 1) * sizeof(int));
        (*values)->dimSize = dimSize;
        for(int i = 0; i < dimSize; ++i)
            (*values)->elt[i] = i;
        return 1;
    }
    

    test.py

    from ctypes import *
    
    class TD2(Structure):
        _fields_ = (('dimSize', c_int),
                    ('elt', c_int * 1))  # correct way to make an array
    
        def __repr__(self): # make class be able to display itself
            return f'TD2(dimSize={self.dimSize})'
    
    dll = CDLL('./test')
    
    # Match arguments and return value to C types.  Note the **TD2 equivalent
    dll.open_device.argtypes = POINTER(c_int),
    dll.open_device.restype = None
    dll.get_sensor_values.argtypes = POINTER(c_int), POINTER(c_ulong), POINTER(POINTER(TD2))
    dll.get_sensor_values.restype = c_int
    
    n = c_int()
    dll.open_device(byref(n)) 
    print(f'from open: n={n.value}')
    
    ts = c_ulong()
    td2 = POINTER(TD2)() # storage for the allocated pointer
    ret = dll.get_sensor_values(byref(n), byref(ts), byref(td2))
    print(f'from get: n={n.value} ts={ts.value} {td2.contents}')
    
    # This is easier to do in C by indexing off the end of the array, but
    # Python doesn't allow that, so to safely read the array in Python the
    # address of the 'elt' can be cast to a pointer to an array of the
    # correct size, then dereferenced and converted to a Python list
    # to see the values.
    data = list(cast(byref(td2.contents.elt), POINTER(c_int * td2.contents.dimSize)).contents)
    print(data)
    

    输出:

    from open: n=5
    from get: n=6 ts=123 TD2(dimSize=10)
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    【讨论】:

      猜你喜欢
      • 2016-01-27
      • 1970-01-01
      • 2011-11-19
      • 2011-06-01
      • 1970-01-01
      • 2010-11-20
      • 2019-09-23
      • 2014-05-21
      相关资源
      最近更新 更多