近日在调用一个厂家给的DLL库时,总是无法调用,后面经过多方百度,发现是因为DLL源码里缺了个“Extern C”(“Extern C”的作用是啥各位请移步度娘或者Google),导致DLL里面的函数名都经过了C++的修饰,只能供C++程序调用。哎我的天啊,自己写的脚本是Python,无法调用这个DLL,任务就完不成,绩效就没有,年终奖就打折了。。。似个愁啊。。。某夜,辗转入睡,梦中迷迷糊糊间貌似用了一个折中的方法,貌似还成功。激动得凌晨2~3点就睡不着了。第二天早上屁颠屁颠的试了下,居然成功,接下来,就让我细细讲解~~~
在调用DLL的时候,首先我们得先知道DLL里面都有哪些函数接口,正常的都会有一个文档来说明。一般来说,文档内的函数名和DLL内部的函数应该是一致的,这样才会方便调用者调用,但是,对于使用C++编写的DLL,如果缺少这个“Extern C”,编译链接时,C++会按照自己的规则篡改函数的名称,这会导致不同的编译器、不同的语言下调用dll发生问题。来看看我用到的这个DLL的文档源码头文件定义:
ARBNETZREMOTE_API int AddDataFile( unsigned int id, int layer, double delta, double voltage, double current );
ARBNETZREMOTE_API int AddDataFile( unsigned int id, int layer, double delta, double voltage, double current,int typ, double f1, double f2, double amp1, double amp2, double phi, int an );
ARBNETZREMOTE_API int AddDataFileVoltage( unsigned int id, double delta, double voltage );
ARBNETZREMOTE_API int AddDataFileCurrent( unsigned int id, double delta, double current );
ARBNETZREMOTE_API int ImportFile( unsigned int id, int layer, char* pName );
ARBNETZREMOTE_API int GetLayers(unsigned int id, int* layer);
ARBNETZREMOTE_API int GetLayersInfo(unsigned int id, int* layer, int* DataFile);
然后在使用工具查看实际的函数,如下:
可以看到,在DLL中,函数名里多了很多符号,这就导致我们在使用Ctypes调用里面的函数时,就没法根据函数名来进行调用了。因为这些函数名在python的语法中都是非法的。怎么办呢?咱们继续。
对了,在这里大家也看到了,首先还得看看DLL里面的函数名是啥。有很多工具,想visual studio的同学来说,可以直接使用dumpbin命令来查看,使用其他的可以使用dllexp,hap-depends来查看,在这里使用的是hap-depends。
接下来就要说到这个看起来平平无奇的函数了——getattr。没错,就是getattr,可以直接动态的调用实例中的属性。
完整的调用过程:
from ctypes import *
sibo = WinDLL('D:\PythonWorks\WaveMaster\WaveMaster_CPP_x64.dll')
openfile = getattr(sibo, '?OpenFile@@YAHPAIPAD@Z')
至此,上述代码中的openfile将具有DLL中OpenFile的所有属性了,openfile将成为一个Python可直接调用的函数API,当然了,调用格式还得按照ctypes的格式来使用。
要说的就这么多了,剩下的大家应该都懂了的。
(后面在仔细看看官方文档,发现里面居然也有这个注释~~~~捂脸仰天)