
ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python.
ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。
在使用Python开发应用程序的时候,往往不可避免的需要用到其他语言编写的库中的函数,例如需要C语言库做一些效率上的补充,或者不同语言系统间做数据交互。使用python中的ctypes库可以很方便的调用windows的dll或linux下的so文件。
现在来简单的介绍一下ctypes的用法。
1.引入ctypes库
1 |
from ctypes import * |
2.假设有了一个用于做加法的DLL(add.dll),该DLL有一个符合cdecl调用约定(这里强调调用约定是因为,stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的)的导出函数Add。
1 2 3 4 5 |
from ctypes import * dll = CDLL("add.dll") print dll.Add(1, 102) #Output is 103. |
调用流程:
1.加载DLL
加载的方式要根据你将要调用的函数是符合什么调用约定的。
stdcall调用约定:两种加载方式
1 2 |
Objdll = ctypes.windll.LoadLibrary("dllpath") Objdll = ctypes.WinDLL("dllpath") |
cdecl调用约定:也有两种加载方式
1 2 |
Objdll = ctypes.cdll.LoadLibrary("dllpath") Objdll = ctypes.CDLL("dllpath") |
windll和cdll分别是WinDLL类和CDll类的对象。
2.调用dll中方法
加载dll的时候会返回一个DLL对象(假设名字叫Objdll),利用该对象调用dll中的方法。
1 |
nRet = Objdll.Add(12, 15) |
假设函数需要你传入一个int类型的指针(int*),可以通过库中的byref关键字来实现,假设现在调用的函数的第三个参数是个int类型的指针。
1 2 3 |
intPara = c_int(9) dll.sub(23, 102, byref(intPara)) print intPara.value |
如果是要传入一个char缓冲区指针,和缓冲区长度,方法至少有四种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# 方法1 szPara = create_string_buffer('/0'*100) dll.PrintInfo(byref(szPara), 100); print szPara.value # 方法2 sBuf = 'aaaaaaaaaabbbbbbbbbbbbbb' pStr = c_char_p( ) pStr.value = sBuf #pVoid = ctypes.cast( pStr, ctypes.c_void_p ).value dll.PrintInfo(pStr, len(pStr.value)) print pStr.value # 方法3 strMa = "/0"*20 FunPrint = dll.PrintInfo FunPrint.argtypes = [c_char_p, c_int] #FunPrint.restypes = c_void_p nRst = FunPrint(strMa, len(strMa)) print strMa,len(strMa) # 方法4 pStr2 = c_char_p("/0") print pStr2.value #pVoid = ctypes.cast( pStr, ctypes.c_void_p ).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属性访问。
1 2 3 |
pchar = dll.getbuffer() szbuffer = c_char_p(pchar) print szbuffer.value |
5.处理C中的结构体类型
在python里面申明一个类似c的结构体,要用到类,并且这个类必须继承自Structure。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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; } |
1 2 3 4 5 6 7 8 9 10 11 12 |
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)) |
如果结构体里面有指针,甚至是指向结构体的指针,处理起来会复杂很多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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; } |
1 2 3 4 5 6 7 8 9 10 11 12 |
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 |
本文大部分搬运自:Python调用windows下DLL详解 – ctypes库的使用
本文链接地址: ctypes 库 — 在 Python 中调用动态链接库
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
如果您愿意为文章的内容或想法提供支持,欢迎点击下边的捐赠按钮,资助作者创作更多高价值高品质的内容。