Python调用C/C++动态链接库的方法详解
本文以实例讲解了Python调用C/C++DLL动态链接库的方法,具体示例如下:
示例一:
首先,在创建一个DLL工程(本例创建环境为VS2005),头文件:
//hello.h #ifdefEXPORT_HELLO_DLL #defineHELLO_API__declspec(dllexport) #else #defineHELLO_API__declspec(dllimport) #endif extern"C" { HELLO_APIintIntAdd(int,int); }
CPP文件:
//hello.cpp #defineEXPORT_HELLO_DLL #include"hello.h" HELLO_APIintIntAdd(inta,intb) { returna+b; }
这里有两个注意点:
(1)弄清楚编译的时候函数的调用约定采用的__cdecl还是__stdcall,因为根据DLL中函数调用约定方式,Python将使用相应的函数加载DLL。
(2)如果采用C++的工程,那么导出的接口需要extern"C",这样python中才能识别导出的函数。
我的工程中采用__cdecl函数调用约定方式进行编译链接产生hello.dll,然后Python中采用ctypes库对hello.dll进行加载和函数调用:
fromctypesimport* dll=cdll.LoadLibrary('hello.dll'); ret=dll.IntAdd(2,4); printret;
至此,第一个小例子已经完成了,读者可以自己动手尝试一下运行效果。
示例二:
示例一只是一个"helloworld"级别的程序,实际运用中更多的需要传递数据结构、字符串等,才能满足我们的需求。那么本示例将展示,如何传递数据结构参数,以及如何通过数据结构获取返回值。
首先编写DLL工程中的头文件:
//hello.h #ifdefEXPORT_HELLO_DLL #defineHELLO_API__declspec(dllexport) #else #defineHELLO_API__declspec(dllimport) #endif #defineARRAY_NUMBER20 #defineSTR_LEN20 structStructTest { intnumber; char*pChar; charstr[STR_LEN]; intiArray[ARRAY_NUMBER]; }; extern"C" { //HELLO_APIintIntAdd(int,int); HELLO_APIchar*GetStructInfo(structStructTest*pStruct); }
CPP文件如下:
//hello.cpp #include<string.h> #defineEXPORT_HELLO_DLL #include"hello.h" HELLO_APIchar*GetStructInfo(structStructTest*pStruct) { for(inti=0;i<ARRAY_NUMBER;i++) pStruct->iArray[i]=i; pStruct->pChar="hellopython!"; strcpy(pStruct->str,"helloworld!"); pStruct->number=100; return"justOK"; }
GetStructInfo这个函数通过传递一个StructTest类型的指针,然后对对象中的属性进行赋值,最后返回"justOK".
编写Python调用代码如下,首先在Python中继承Structure构造一个和CDLL中一致的数据结构StructTest,然后设置函数GetStructInfo的参数类型和返回值类型,最后创建一个StructTest对象,并将其转化为指针作为参数,调用函数GetStrcutInfo,最后通过输出数据结构的值来检查是否调用成功:
fromctypesimport* ARRAY_NUMBER=20; STR_LEN=20; #definetype INTARRAY20=c_int*ARRAY_NUMBER; CHARARRAY20=c_char*STR_LEN; #definestruct classStructTest(Structure): _fields_=[ ("number",c_int), ("pChar",c_char_p), ("str",CHARARRAY20), ("iArray",INTARRAY20) ] #loaddllandgetthefunctionobject dll=cdll.LoadLibrary('hello.dll'); GetStructInfo=dll.GetStructInfo; #setthereturntype GetStructInfo.restype=c_char_p; #settheargtypes GetStructInfo.argtypes=[POINTER(StructTest)]; objectStruct=StructTest(); #invokeapiGetStructInfo retStr=GetStructInfo(byref(objectStruct)); #checkresult print"number:",objectStruct.number; print"pChar:",objectStruct.pChar; print"str:",objectStruct.str; fori,valinenumerate(objectStruct.iArray): print'Array[i]:',val; printretStr;
总结:
1.用64位的Python去加载32位的DLL会出错
2.以上只是些测试程序,在编写Python过程中尽可能的使用"tryExcept"来处理异常
3.注意在Python与CDLL交互的时候字节对齐问题
4.ctypes库的功能还有待继续探索