从python调用c函数不起作用

时间:2014-02-14 02:15:21

标签: python c ctypes

我几乎可以肯定我一年前做过一次这样的事情......不仅仅是行不通。奇怪的。我必须在某个地方犯一个小错误......请帮助!

我有以下玩具代码:

// testdll.c
int sum(int a, int b)
{
    return(a+b);
}

然后,由于我使用的是Windows 7,我使用WinSDK 7.1的x64 C / C ++编译器进行编译:

cl testdll.c /TC /LD

输出是testdll.dll。

然后,在我的Python 3.3中,我使用了:

In [12]: import ctypes

In [13]: lib = ctypes.cdll.LoadLibrary('./testdll.dll')

In [14]: lib
Out[14]: <CDLL './testdll.dll', handle f7000000 at a43ea58>

In [15]: lib.sum
Traceback (most recent call last):

  File "<ipython-input-15-309017dbbec8>", line 1, in <module>
    lib.sum

  File "C:\WinPython2\python-2.7.6.amd64\lib\ctypes\__init__.py", line 378, in __getattr__
    func = self.__getitem__(name)

  File "C:\WinPython2\python-2.7.6.amd64\lib\ctypes\__init__.py", line 383, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))

AttributeError: function 'sum' not found

找不到这个功能!这让我疯狂。由于我使用了纯C,并且在编译期间使用了/ TC,因此它不应该是名称错误问题。

任何想法都会受到赞赏。非常感谢你们!

EDIT 2014/02/13 我试着用gcc编译它,但没有运气。发生同样的老问题。 我在python中使用了dir(),并且意识到一切都应该在其中 - 只是不正确的名称,我的意思是它不能通过fun.sum调用。它能够识别函数的结果类型是int。

In [34]: dir(lib)
Out[34]: 
['_FuncPtr',
 '__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattr__',
 '__getattribute__',
 '__getitem__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_func_flags_',
 '_func_restype_',
 '_handle',
 '_name']

In [35]: lib._func_restype_
Out[35]: ctypes.c_long

1 个答案:

答案 0 :(得分:3)

导出符号的一种方法是通过链接器的选项:

   
cl testdll.c /LD /link /export:sum

这不会扩展。更好的选择是在声明中使用__declspec(dllexport)修饰符。请参阅Visual Studio文档中的Exporting from a DLL

有条件地应用如下:

   
#ifdef BUILD_TESTDLL
#define TESTAPI __declspec(dllexport)
#else
#define TESTAPI __declspec(dllimport)
#endif

/* declaration */
TESTAPI int sum(int a, int b);

/* definition */
int sum(int a, int b)
{
    return(a+b);
}

通过导入lib链接时,声明应使用__declspec(dllimport),这是TESTAPI的默认值。构建DLL时,请在项目设置中或通过BUILD_TESTDLL在命令行中定义/D

最后,为了获得最大的灵活性,请使用如下的.def文件:

   
LIBRARY TESTDLL
EXPORTS
    sum @ 10 NONAME 
    sum_alias=sum @ 20

这允许您使用其他名称导出函数,或仅按顺序导出。将其添加为源文件,如下所示:

cl testdll.c testdll.def /LD

然后在Python中,例如:

   
>>> from ctypes import *
>>> lib = cdll.testdll
>>> lib.sum_alias(1, 2)
3
>>> lib[10](1, 2)      
3
>>> lib[20](1, 2)
3

顺便说一下,ctypes不会从DLL预加载导出。 dir(lib)一开始不会显示任何函数指针。他们在访问时被缓存:

>>> from ctypes import *
>>> lib = cdll.testdll
>>> sorted(vars(lib))
['_FuncPtr', '_handle', '_name']
>>> lib.sum_alias(1, 2)
3
>>> sorted(vars(lib))
['_FuncPtr', '_handle', '_name', 'sum_alias']