我正在为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
答案 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。