在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分数据的交互。使用python中的ctypes模块可以很方便的调用windows的dll(也包括linux下的so等文件),下面将详细的讲解这个模块(以windows平台为例子),当然我假设你们已经对windows下怎么写一个DLL是没有问题的。 引入ctypes库
假设你已经有了一个的DLL(名字是add.dll),且该DLL有一个符合cdecl(这里强调调用约定是因为,stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的,后面会有说明)调用约定的导出函数Add。 建立一个Python文件DllCall.py测试:
from ctypes import * dll = CDLL("add.dll" ) print dll.Add( 1 , 102 )
结果:103
上面是一个简单的例子。下面简单聊一下调用流程: 1、加载DLL 上面已经说过,加载的时候要根据你将要调用的函数是符合什么调用约定的。 stdcall调用约定:两种加载方式
Objdll = ctypes.windll.LoadLibrary( "dllpath" ) Objdll = ctypes.WinDLL("dllpath" )
cdecl调用约定:也有两种加载方式
Objdll = ctypes.cdll.LoadLibrary( "dllpath" ) Objdll = ctypes.CDLL("dllpath" ) <span style="font-family:Microsoft YaHei;" >其实windll和cdll分别是WinDLL类和CDll类的对象。</span>
2、调用dll中的方法 在1中加载dll的时候会返回一个DLL对象(假设名字叫Objdll),利用该对象就可以调用dll中的方法。 e.g.如果dll中有个方法名字叫Add(注意如果经过stdcall声明的方法,如果不是用def文件声明的导出函数或者extern “C” 声明的话,编译器会对函数名进行修改,这个要注意,我想你们懂的。) 调用:nRet = Objdll.Add(12, 15) 即完成一次调用。 看起来调用似乎很简单,不要只看表象,呵呵,这是因为Add这个函数太简单了,现在假设函数需要你传入一个int类型的指针(int*),可以通过库中的byref关键字来实现,假设现在调用的函数的第三个参数是个int类型的指针。
intPara = c_int( 9 ) dll.sub(23 , 102 , byref(intPara)) print intPara.value
如果是要传入一个char缓冲区指针,和缓冲区长度,方法至少有四种:
szPara = create_string_buffer('/0' * 100 ) dll.PrintInfo(byref(szPara), 100 ); print szPara.value sBuf = 'aaaaaaaaaabbbbbbbbbbbbbb' pStr = c_char_p( ) pStr.value = sBuf dll.PrintInfo(pStr, len(pStr.value)) print pStr.value strMa = "/0" * 20 FunPrint = dll.PrintInfo FunPrint.argtypes = [c_char_p, c_int] nRst = FunPrint(strMa, len(strMa)) print strMa,len(strMa) pStr2 = c_char_p("/0" ) print pStr2.value dll.PrintInfo(pStr2, len(pStr.value)) print pStr2.value
3、C基本类型和ctypes中实现的类型映射表 ctypes数据类型 C数据类型 c_char char c_short short c_int int c_long long c_ulong unsign long c_float float c_double double c_void_p void 对应的指针类型是在后面加上"_p",如int*是c_int_p等等。 在python中要实现c语言中的结构,需要用到类。 4、DLL中的函数返回一个指针。 虽然这不是个好的编程方法,不过这种情况的处理方法也很简单,其实返回的都是地址,把他们转换相应的python类型,再通过value属性访问。
pchar = dll.getbuffer() szbuffer = c_char_p(pchar) print szbuffer.value
5、处理C中的结构体类型 为什么把这个单独提出来说呢,因为这个是最麻烦也是最复杂的,在python里面申明一个类似c的结构体,要用到类,并且这个类必须继承自Structure。 先看一个简单的例子: C里面dll的定义如下:
typedef struct _SimpleStruct { int nNo; float fVirus; char szBuffer[512]; } SimpleStruct, *PSimpleStruct; typedef const SimpleStruct* PCSimpleStruct; extern "C" int __declspec ( dllexport ) PrintStruct(PSimpleStruct simp); int PrintStruct(PSimpleStruct simp) { printf ("nMaxNum=%f, szContent=%s" , simp->fVirus, simp->szBuffer); return simp->nNo; }
Python的定义:
from ctypes import * class SimpStruct(Structure): _fields_ = [ ("nNo" , c_int), ("fVirus" , c_float), ("szBuffer" , c_char * 512 )] dll = CDLL("AddDll.dll" ) simple = SimpStruct(); simple.nNo = 16 simple.fVirus = 3.1415926 simple.szBuffer = "magicTong/0" print dll.PrintStruct(byref(simple))
上面例子结构体很简单,但是如果结构体里面有指针,甚至是指向结构体的指针,处理起来会复杂很多,不过Python里面也有相应的处理方法,下面这个例子来自网上,本来想自己写个,懒得写了,能说明问题就行: C代码如下:
typedef struct { char words[10]; }keywords; typedef struct { keywords *kws; unsigned int len; }outStruct; extern "C" int __declspec ( dllexport ) test(outStruct *o); int test(outStruct *o) { unsigned int i = 4; o->kws = (keywords *)malloc(sizeof (unsigned char ) * 10 * i); strcpy(o->kws[0].words, "The First Data" ); strcpy(o->kws[1].words, "The Second Data" ); o->len = i; return 1; }
Python代码如下:
class keywords(Structure): _fields_ = [('words' , c_char * 10 ),] class outStruct(Structure): _fields_ = [('kws' , POINTER(keywords)), ('len' , c_int),] o = outStruct() dll.test(byref(o)) print o.kws[ 0 ].words; print o.kws[ 1 ].words; print o.len
6、例子 说得天花乱坠,嘿嘿,还是看两个实际的例子。 例子1: 这是一个GUID生成器,其实很多第三方的python库已经有封装好的库可以调用,不过这得装了那个库才行,如果想直接调用一些API,对于python来说,也要借助一个第三方库才行,这个例子比较简单,就是用C++调用win32 API来产生GUID,然后python通过调用C++写的dll来获得这个GUID。 C++代码如下:
extern "C" __declspec ( dllexport ) char * newGUID(); char * newGUID() { static char buf[64] = {0}; statc GUID guid; if (S_OK == ::CoCreateGuid(&guid)) { _snprintf(buf, sizeof (buf), "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" , guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7] ); ::MessageBox(NULL, buf, "GUID" , MB_OK); } return ( char *)buf; }
Python代码如下:
def CreateGUID(): try : strDllPath = sys.path[0 ] + str(os.sep) + "createguid.dll" dll = CDLL(strDllPath) b = dll.newGUID() a = c_char_p(b) except Exception, error: print error return "" return a.value
例子2: 这个例子是调用kernel32.dll中的createprocessA函数来启动一个记事本进程
from ctypes import * class _PROCESS_INFORMATION(Structure): _fields_ = [('hProcess' , c_void_p), ('hThread' , c_void_p), ('dwProcessId' , c_ulong), ('dwThreadId' , c_ulong)] class _STARTUPINFO(Structure): _fields_ = [('cb' ,c_ulong), ('lpReserved' , c_char_p), ('lpDesktop' , c_char_p), ('lpTitle' , c_char_p), ('dwX' , c_ulong), ('dwY' , c_ulong), ('dwXSize' , c_ulong), ('dwYSize' , c_ulong), ('dwXCountChars' , c_ulong), ('dwYCountChars' , c_ulong), ('dwFillAttribute' , c_ulong), ('dwFlags' , c_ulong), ('wShowWindow' , c_ushort), ('cbReserved2' , c_ushort), ('lpReserved2' , c_char_p), ('hStdInput' , c_ulong), ('hStdOutput' , c_ulong), ('hStdError' , c_ulong)] NORMAL_PRIORITY_CLASS = 0x00000020 kernel32 = windll.LoadLibrary("kernel32.dll" ) CreateProcess = kernel32.CreateProcessA ReadProcessMemory = kernel32.ReadProcessMemory WriteProcessMemory = kernel32.WriteProcessMemory TerminateProcess = kernel32.TerminateProcess ProcessInfo = _PROCESS_INFORMATION() StartupInfo = _STARTUPINFO() fileName = 'c:/windows/notepad.exe' address = 0x0040103c strbuf = c_char_p("_" ) bytesRead = c_ulong(0 ) bufferSize = len(strbuf.value) CreateProcess(fileName, 0 , 0 , 0 , 0 , NORMAL_PRIORITY_CLASS, 0 , 0 , byref(StartupInfo), byref(ProcessInfo))
转自 http://blog.csdn.net/magictong/article/details/3075478