为什么设置ctypes dll.function.restype = c_void_p返回长? [英] Why does setting ctypes dll.function.restype=c_void_p return long?
问题描述
奇怪的是,即使设置了 restype
,python也会返回 long
,而不是 c_void_p
。
It seems odd that even after setting restype
, python returns long
rather than c_void_p
.
例如;
# 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; }
我最近不得不修复一个错误,该错误会给 x
返回另一个ctypes函数,指针被践踏。通过将初始dll调用更改为
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())
...我猜想沿线某处的ctypes x
...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
用于 P指针类型使用 PyLong_FromVoidPtr
。如果地址适合在平台 long
中,则返回Python int
;否则,它将返回精度为可变的Python long
。很好,但是当将此整数值作为参数传递时,默认行为是转换为C int
,在所有支持的平台上均为32位。
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.
我认为最好的解决方案是设置 argtypes
正确地将参数转换为指针类型。另一种选择是将 restype
设置为 c_void_p
的子类。使用子类会禁止转换为Python整数。 GetResult
通过调用 _ctypes_simple_instance
,实际上返回其名称和源注释所建议的相反。 (在2.5中,此函数名为 IsSimpleSubType
,然后源注释也是错误的,所讨论的简单绝不是元类 PyCSimpleType
,但基本类型为 _SimpleCData
。)
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
。
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'>
这篇关于为什么设置ctypes dll.function.restype = c_void_p返回长?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!