使用ctypes指定结果类型时,从python调用外部库返回错误结果

问题描述

我有一个带有函数的外部C库

extern ushort* getSomeStringW(ushort* Dest,int MaxLen);

函数更改Dest缓冲区并返回。 我用ctypes包装它。由于该库始终返回16位编码的字符串,因此我必须显式使用c_ushortc_uint16缓冲区,而不能仅使用ctypes c_wchar_t方法创建create_unicode_buffer缓冲区。 (c_wchar_t在Win上是16位,在OSX上是32位)

因此,我这样调用库:

dest = (ctypes.c_uint16 * MaxLen)()
result = library.getSomeStringW(byref(dest),MaxLen)

这按预期工作。有趣的是,ctypes似乎猜测返回类型,结果是一个python字符串。之后,我还可以访问dest缓冲区中的内容,并将其显式解码为utf-16。

尽管如此,我仍然考虑过指定函数restype,但奇怪的是,此后结果还是错误的。

如果我使用上面的代码代替上面的代码

dest = (ctypes.c_uint16 * MaxLen)()
library.getSomeStringW.restype = ctypes.c_uint16 * MaxLen
result = library.getSomeStringW(byref(dest),MaxLen)

result数组看起来与dest数组不同。我知道数组可能没有引用相同的内存位置(至少这是我对ctypes文档所做的操作),但我仍然不明白为什么结果ctypes对象未正确填充。

当我使用numpy ctypelib并将restype声明为

时,它可以工作
import numpy as np

library.getSomeStringW.restype = np.ctypeslib.ndpointer(ctypes.c_uint16,shape=(MaxLen,))

...这让我更加困惑。

在无法正常工作的情况下,dest数组看起来就像我期望的那样

0000 = {int} 52
0001 = {int} 46
0002 = {int} 49
0003 = {int} 48
0004 = {int} 46
0005 = {int} 50
0006 = {int} 46
0007 = {int} 50
0008 = {int} 56
0009 = {int} 56
0010 = {int} 53
0011 = {int} 0
0012 = {int} 0
...
1000 = {int} 0

result数组看起来像

0000 = {int} 53920
0001 = {int} 25850
0002 = {int} 321
0003 = {int} 0
0004 = {int} 63968
0005 = {int} 35350
0006 = {int} 32764
0007 = {int} 0
0008 = {int} 52816
0009 = {int} 47565
0010 = {int} 32764
0011 = {int} 0
0012 = {int} 39040
0013 = {int} 26425
0014 = {int} 321
0015 = {int} 0
...

每个第4个整数似乎都为0,这些值遍地都是(显然不是明智的utf-16字符值)

感谢您可能有任何想法!

解决方法

在注意到每次我运行代码时结果数组都会改变之后,我意识到我没有将结果设置为指针。

当然,numpy函数还会创建一个指针。

library.getSomeStringW.restype = ctypes.POINTER(ctypes.c_uint16 * MaxLen)

解决所有问题。