45

I have a DLL containing a C function with a prototype like this:

int c_read_block(uint32 addr, uint32 *buf, uint32 num);

I want to call it from Python using ctypes. The function expects a pointer to a chunk of memory, into which it will write the results. I don't know how to construct and pass such a chunk of memory. The ctypes documentation isn't much help.

Constructing an array and passing it "byref", like this:

    cresult = (c_ulong * num)()
    err = self.c_read_block(addr, byref(cresult), num)

Gives this error message:

ArgumentError: argument 3: <type 'exceptions.TypeError'>: expected LP_c_ulong instance instead of pointer to c_ulong_Array_2

I guess that is because the Python ulong array is nothing like a c uint32 array. Should I use create_char_string. If so, how do I persuade Python to "cast" that buffer to an LP_c_ulong?

3 Answers 3

68

You can cast with the cast function :)

>>> import ctypes
>>> x = (ctypes.c_ulong*5)()
>>> x
<__main__.c_ulong_Array_5 object at 0x00C2DB20>
>>> ctypes.cast(x, ctypes.POINTER(ctypes.c_ulong))
<__main__.LP_c_ulong object at 0x0119FD00>
>>> 
Sign up to request clarification or add additional context in comments.

4 Comments

I've used ctypes a few times and I've encountered it in the linked documentation before.
Why i it a LP_c_ulong object and not LP_c_ulong_Array_5 object
LP_c_ulong_Array_5 would be an address pointing to an address pointing to the first element in an array of uint32s, whereas the OP's function is looking to receive an address pointing to the first element of an array. LP_c_ulong_Array_5 would be for a function like: int c_read_block(uint32 addr, uint32 *buf[], uint32 num);
Better declare the .argtypes on the C function in question, then ctypes should be able to implicitly handle the array. In that case, you also don't need to think of using byref, see docs.python.org/3/library/ctypes.html#type-conversions
4

You can cast the result, but ctypes allows you to use an array in place of a pointer, directly. The issue is the byref in your code (which would be the equivalent of a pointer to a pointer):

So instead of:

cresult = (c_ulong * num)()
err = self.c_read_block(addr, byref(cresult), num)

try:

cresult = (c_ulong * num)()
err = self.c_read_block(addr, cresult, num)

Comments

-3

There is a typo in the solution. In order to get a pointer to an array of ulongs you need to cast to a POINTER(list of ulong)

In [33]: ptr = ctypes.cast(x, ctypes.POINTER(ctypes.c_ulong*5))
In [34]: ptr
Out[34]: <__main__.LP_c_ulong_Array_5 at 0x23e2560>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.