Pointers and arrays in Python ctypes
Question:
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?
Answers:
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>
>>>
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>
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)
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?
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>
>>>
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>
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)