It seems odd that even after setting restype
, python returns long
rather than c_void_p
.
For example;
# python code
from ctypes import *
dll = windll.LoadLibrary("my.dll")
dll.my_object_create.restype = c_void_p
x = dll.my_object_create()
print type(x) # prints <type 'long'>
//c++ code
my_object *my_object_create() { return new my_object(); }
void my_object_destroy(my_object *obj) { delete obj; }
I recently had to fix a bug where, feeding x
back to another ctypes function, the pointer got trampled. This was fixed by changing the initial dll call to
x = c_void_p(dll.my_object_create())
...I'm guessing somewhere along the line ctypes treated x
as 4 bytes long not 8 (64 bit architecture).
So I am wondering if there is a reason why the existing behaviour leads you into this trap?
P_get
for the 'P' pointer type uses PyLong_FromVoidPtr
. If the address fits in a platform long
, it returns a Python int
; otherwise it returns a Python long
, which has variable precision. That's fine, but when passing this integer value as an argument, the default behavior is to convert to a C int
, which is 32-bit on all supported platforms.
I think the best solution is to set argtypes
to properly convert an argument to a pointer type. Another option is to set restype
to a subclass of c_void_p
. Using a subclass disables the conversion to a Python integer. GetResult
checks this by calling _ctypes_simple_instance
, which actually returns the opposite of what its name and the source comment suggest. (In 2.5 this function was named IsSimpleSubType
, and the source comment was wrong back then too. The "simple" in question was never the metaclass PyCSimpleType
, but the base type _SimpleCData
.)
POSIX:
# Configure the interpreter to load visible extension-
# module symbols, such as _ctypes_simple_instance,
# into the global symbol table.
import sys, DLFCN
sys.setdlopenflags((sys.getdlopenflags() & ~DLFCN.RTLD_LOCAL) |
DLFCN.RTLD_GLOBAL)
from ctypes import *
_ctypes_simple_instance = PyDLL(None)._ctypes_simple_instance
_ctypes_simple_instance.argtypes = py_object,
malloc = CDLL(None).malloc
class my_void_p(c_void_p):
pass
>>> _ctypes_simple_instance(c_void_p)
0
>>> _ctypes_simple_instance(my_void_p)
1
>>> malloc.restype = c_void_p
>>> type(malloc(100))
<type 'int'>
>>> malloc.restype = my_void_p
>>> type(malloc(100))
<class '__main__.my_void_p'>
Windows:
_ctypes_simple_instance
isn't exported by _ctypes.pyd.
from ctypes import *
malloc = cdll.msvcrt.malloc
class my_void_p(c_void_p):
pass
>>> malloc.restype = c_void_p
>>> type(malloc(100))
<class 'int'>
>>> malloc.restype = my_void_p
>>> type(malloc(100))
<class '__main__.my_void_p'>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With