指向Ctypes

时间:2016-06-07 13:14:14

标签: python c pointers dll ctypes

我正在为Python Ctypes编写C dll的包装器。在C库中,我有一种类型的功能

int32 Transfer  ( ..., PIN_PARAMS_TRANSFERDATA  pInData, ... ) 

其中PIN_PARAMS_TRANSFERDATA是指向此结构的指针:

typedef struct
{
    ...
    void *  pDataBuffer;        //!< Pointer to application-allocated buffer to hold requested data.
} IN_PARAMS_TRANSFERDATA, *PIN_PARAMS_TRANSFERDATA;

DataBuffer依赖的数据类型可以更改,但在我的情况下通常为int16。在ctypes中,我将结构和函数定义如下:

class IN_PARAMS_TRANSFERDATA(Structure):
    _fields_ = [
        ...,
        ('pDataBuffer', c_void_p)
    ]

_lib = CDLL('mydll.dll')

_lib.Transfer.argtypes = [..., POINTER(IN_PARAMS_TRANSFERDATA), ...]
_lib.Transfer.restype = c_int32

def Transfer(system, in_data):
    status = _lib.Transfer(..., byref(in_data), ...)
    return in_data

现在我遇到了在调用Transfer函数之前必须定义数据缓冲区的问题,我无法管理。我尝试了以下(2000只是用于测试,而应该是一个变量):

in_data = IN_PARAMS_TRANSFERDATA()
data_buffer = (c_int16*2000)()
print(data_buffer)
in_data.pDataBuffer = byref(data_buffer)
in_data = Transfer(hsystem, in_data)

输出消息是:

<__main__.c_short_Array_2000 object at 0x00000000053D2EC8>
Traceback (most recent call last):
  File ".../test.py", line 48, in <module>
    in_data.pDataBuffer = byref(data_buffer)
TypeError: cannot be converted to pointer

任何人都可以帮助我,如何正确创建数据缓冲区并将其传递给结构(最后传递给C函数),这样C函数就可以将数据写入其中并且我可以读出数据(例如as一个numpy.array)。

如果这有帮助,这就是在一个示例C程序中创建缓冲区指针的方式(遗憾的是我真的不明白发生了什么):

void* pBuffer = NULL;
pBuffer  = VirtualAlloc(NULL, (size_t)((i64TransferLength + i64Padding) * u32SampleSize), MEM_COMMIT, PAGE_READWRITE);

修改

根据J.F. Sebastian的建议,我稍微改变了缓冲区的创建:

data_buffer = (c_int16*2000)()
p_data_buffer = ctypes.cast(data_buffer, POINTER(c_int16*2000))
in_data.pDataBuffer = p_data_buffer

现在给我一个稍微不同的错误:

Traceback (most recent call last):
  File ".../test.py", line 50, in <module>
    in_data.pDataBuffer = p_data_buffer
TypeError: incompatible types, LP_c_short_Array_2000 instance instead of c_void_p instance

2 个答案:

答案 0 :(得分:3)

ctypes.c_void_p表示为整数。你可以传递数组的地址:

>>> from ctypes import *
>>> class IN_PARAMS_TRANSFERDATA(Structure):
...     _fields_ = [('pDataBuffer', c_void_p)]
... 
>>> ArrayType = c_int16 * 2000
>>> in_data = IN_PARAMS_TRANSFERDATA()
>>> data_buffer = ArrayType(1,2,3) # set the first three values
>>> in_data.pDataBuffer = c_void_p(addressof(data_buffer)) 
>>>                  # or cast(data_buffer, c_void_p)

获取值:

>>> cast(in_data.pDataBuffer, POINTER(ArrayType)).contents[:3]
[1, 2, 3]

这是前三个值。

要从指针获取numpy数组,请参阅How to convert pointer to c array to python array

>>> import numpy
>>> pa = cast(in_data.pDataBuffer, POINTER(ArrayType))
>>> a = numpy.frombuffer(pa.contents, dtype=c_int16)
>>> a
array([1, 2, 3, ..., 0, 0, 0], dtype=int16)

答案 1 :(得分:0)

另一种可能性是使用VirtualAlloc,就像在原始C代码中一样。

p_data_buffer = ctypes.windll.kernel32.VirtualAlloc(c_int(0), c_int(sizeof(c_int16)*2000), c_int(0x00001000), c_int(0x04))
in_data.pDataBuffer = p_data_buffer

为了获取数据:

target = (c_int16*2000)()
ctypes.windll.kernel32.RtlMoveMemory(target, in_data.pDataBuffer, c_int(sizeof(c_int16)*2000))
data = numpy.frombuffer(target, dtype=c_int16)

Here您可以找到更多详细信息。我假设,此代码特定于Windows。