Python 2 与 3 中 ctypes 的区别

17 投票
3 回答
10422 浏览
提问于 2025-04-17 00:53

我有一个在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 个回答

1

根据Python的官方文档,2.7和3.2之间的变化可以在这里找到

新增了一种类型,ctypes.c_ssize_t,表示C语言中的ssize_t数据类型。

在2.7版本中,还引入了一些其他的修改

ctypes模块现在总是将None转换为C语言中的NULL指针,特别是当参数被声明为指针时。(这个改动是由Thomas Heller做的;问题编号4606。)底层的libffi库也更新到了3.0.9版本,修复了不同平台上的一些问题。(这个更新是由Matthias Klose做的;问题编号8142。)

我不确定这能否解释你遇到的问题...

2

在我们的例子中,代码看起来像这样:

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)
22

在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)

撰写回答