如何通过python ctypes将二进制缓冲区传递给c函数

0 投票
1 回答
1310 浏览
提问于 2025-04-16 13:41

我想在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类型的函数。cdllwindll之间的区别就是它们使用的调用约定不同。

那个错误信息其实就是一个经典的调用约定不匹配的错误,值得注意。

撰写回答