erik erik - 2 years ago 244
Python Question

Pointer to c_int16 array buffer in Ctypes

I'm writing a wrapper for a C dll in Python Ctypes. In the C library I have a function of the kind

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

is a pointer to this structure:

typedef struct
void * pDataBuffer; //!< Pointer to application-allocated buffer to hold requested data.

The data type of the
depends can change, but is usually
in my case. In ctypes I defined the structure and the function as follows:

_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

Now I have the problem that I have to define a data buffer before calling the
function, which I don't manage. I tried the following (the 2000 is just for testing and should rather be a variable):

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

The output message is:

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

Can anybody help me, how to create the data buffer properly and pass it to the structure (and finally to the C function), such that the C function can write data into it and I can read the data out (e.g. as a

In case this helps, this is how the creation of the buffer pointer is done in an example C program (unfortunately I don't really understand what's going on):

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


Following J.F. Sebastian's suggestion, I changed the creation of the buffer slightly:

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

which gives me a slightly different error now:

Traceback (most recent call last):
File ".../", 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

Answer Source

ctypes.c_void_p is represented as an integer. You could pass it the address of the array:

>>> from ctypes import *
>>> class IN_PARAMS_TRANSFERDATA(Structure):
...     _fields_ = [('pDataBuffer', c_void_p)]
>>> ArrayType = c_int16 * 2000
>>> 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)

Get the values back:

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

Here're the first three values.

To get numpy array from the pointer, see 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)
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download