如何通过python ctypes将二进制缓冲区传递给c函数
我想在Python 2.x中使用ctypes模块来包装以下的C语言函数bar(),并在Linux和Windows上调用它:
#include <stdio.h>
#ifdef FOO_DLL
#define FOO_API __declspec(dllexport)
#else
#define FOO_API extern
#endif
FOO_API unsigned int bar(const void* data, unsigned int len)
{
printf("%p (%d bytes)\n", data, len);
return len;
}
为了编译这个共享库,我使用scons:
import sys
env = Environment()
if sys.platform == 'win32':
env.Append(CFLAGS=['-DFOO_DLL'])
env.SharedLibrary('foo', ['foo.c'])
最后,我加载相应的共享库并调用这个函数:
import sys
import struct
from ctypes import *
if sys.platform == 'win32':
libfoo = windll.LoadLibrary('foo')
else:
libfoo = cdll.LoadLibrary('./libfoo.so')
data = struct.pack('BBB', 0, 1, 2)
libfoo.bar(data, len(data))
让我惊讶的是,这在Linux下似乎运行得很好,但在Windows上,代码却抛出了一个错误,这让我怀疑这样做是不是不太对:
$ python foo.py
00B520C4 (3 bytes)
Traceback (most recent call last):
File "foo.py", line 11, in <module>
libfoo.bar(data, len(data))
ValueError: Procedure probably called with too many arguments (8 bytes in excess)
那么正确的做法是什么呢?
在Linux(Ubuntu 10.10 64位)上,我使用的是Python 2.6.6和gcc 4.4.5,而在Windows(XP 32位,运行在VirtualBox中)上,我使用的是Python 2.7.1和VS2010 Express。
1 个回答
1
在Windows和Linux系统中,应该使用cdll
,这样就能使用C语言的调用约定。现在的Python代码是用stdcall
来调用你的Windows DLL,但实际上它应该是cdecl
类型的函数。cdll
和windll
之间的区别就是它们使用的调用约定不同。
那个错误信息其实就是一个经典的调用约定不匹配的错误,值得注意。