Python 2 与 3 中 ctypes 的区别
我有一个在Python 2.7下能正常工作的程序,它调用了一个DLL。我现在想把这个程序移植到Python 3.2。调用DLL的部分似乎没有出错(也就是说,调用时没有报错),但返回的数据却不太对劲。
为了方便理解,这里有一些信息:这个调用需要三个参数:两个整数(输入)和一个指向无符号短整型数组的指针(输出)。
我尝试过使用Python和numpy数组,但都没有成功。
有没有人能列出Python 2.7和3.2在ctypes方面的区别?
提前谢谢大家!
编辑
这里有一些示例代码。这个DLL是专有的,所以我没有它的代码。但我有C语言的头文件:
void example (int width, int height, unsigned short* pointer)
Python代码是:
width, height = 40, 100
imagearray = np.zeros((width,height), dtype=np.dtype(np.ushort))
image = np.ascontiguousarray(imagearray)
ptrimage = image.ctypes.data_as(ct.POINTER(ct.c_ushort))
DLL.example(width, height, ptrimage)
这个在Python 2.7下能正常工作,但在3.2下不行。
编辑 2
如果ctypes的变化只是Cedric提到的那些,那Python 3.2不工作的原因就不太合理。所以我再次查看了代码,发现有一个准备函数在我提到的函数之前被调用。它的签名是:
void prepare(char *table)
在Python中,我是这样调用的:
table = str(aNumber)
DLL.prepare(table)
问题可能是由于Python字符串处理的变化导致的吗?
3 个回答
在我们的例子中,代码看起来像这样:
addr = clib.some_function()
data = some_struct.from_address(addr)
这个在Python 2中能正常工作,但在Python 3中就不行了。原因并不是ctypes有什么不同,而是内存布局的变化暴露了上面代码中的一个bug。在Python 2中,返回的地址恰好足够小,可以放进一个C语言的int类型(32位),这是所有ctypes函数调用的默认返回类型。而在Python 3中,返回的地址几乎总是太大,导致指针地址在被强制转换为int时出现了问题。
解决办法是将函数的restype
设置为64位整数类型,以确保它能容纳整个地址,比如:
clib.some_function.restype = c_longlong
或者:
clib.some_function.restype = POINTER(some_struct)
在Python 2.7中,字符串默认是字节字符串,也就是说它们是以字节的形式存储的。而在Python 3.x中,字符串默认是Unicode字符串,这种格式可以支持更多的字符,比如中文、日文等。如果你在使用DLL.prepare
之前,想要确保你的字符串是字节字符串,可以使用.encode('ascii')
来明确地将它转换成字节字符串。
编辑:
#another way of saying table=str(aNumber).encode('ascii')
table = bytes(str(aNumber), 'ascii')
DLL.prepare(table)