在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分数据的交互。使用python中的ctypes模块可以很方便的调用windows的dll(也包括linux下的so等文件),下面将详细的讲解这个模块(以windows平台为例子),当然我假设你们已经对windows下怎么写一个DLL是没有问题的。
引入ctypes库
from ctypes import *
假设你有了一个符合cdecl(这里强调调用约定是因为,stdcall调用约定和cdecl调用约定声明的导出函数,在用python加载使用的加载函数是不同的,后面会说明)调用约定的DLL(名字是add.dll),且有一个导出函数Add。
建立一个Python文件DllCall.py测试:
from
ctypes
import
*
dll
=
CDLL(
"add.dll"
)
print
dll.Add(
1
,
102
)
上面是一个简单的例子。
1、加载DLL
上面已经说过,加载的时候要根据你将要调用的函数是符合什么调用约定的。
stdcall调用约定:两种加载方式
Objdll = ctypes.windll.LoadLibrary("dllpath")
Objdll = ctypes.WinDLL("dllpath")
cdecl调用约定:也有两种加载方式
Objdll = ctypes.cdll.LoadLibrary("dllpath")
Objdll = ctypes.CDLL("dllpath")
其实windll和cdll分别是WinDLL类和CDll类的对象。
2、调用dll中的方法
在1中加载dll的时候会返回一个DLL对象(假设名字叫Objdll),利用该对象就可以调用dll中的方法。
e.g.如果dll中有个方法名字叫Add(注意如果经过stdcall声明的方法,如果不是用def文件声明的导出函数的话,编译器会对函数名进行修改,这个要注意)
调用:nRet = Objdll.Add(12, 15) 即完成一次调用。
看起来调用似乎很简单,不要只看表象,呵呵,这是因为Add这个函数太简单了,现在假设函数需要你传入一个int类型的指针(int*),可以通过库中的byref关键字来实现,假设现在调用的函数的第三个参数是个int类型的指针。
intPara
=
c_int(
9
)
dll.sub(
23
,
102
, byref(intPara))
print
intPara.value
# char* -- 1
szPara
=
create_string_buffer(
'\0'
*
100
)
dll.PrintInfo(byref(szPara),
100
);
print
szPara.value
# char* -- 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
# char* -- 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)
# char* -- 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属性访问。
pchar = dll.getbuffer()
szbuffer = c_char_p(pchar)
print szbuffer.value
5、处理C中的结构体类型
为什么把这个单独提出来说呢,因为这个是最麻烦也是最复杂的,在python里面申明一个类似c的结构体,要用到类,并且这个类必须继承自Structure。
先看一个简单的例子:
C里面dll的定义如下:
01
typedef
struct
_SimpleStruct
02
{
03
int
nNo;
04
float
fVirus;
05
char
szBuffer[512];
06
} SimpleStruct, *PSimpleStruct;
07
typedef
const
SimpleStruct* PCSimpleStruct;
08
extern
"C"
int
__declspec
(
dllexport
) PrintStruct(PSimpleStruct simp);
09
int
PrintStruct(PSimpleStruct simp)
10
{
11
printf
(
"nMaxNum=%f, szContent=%s"
, simp->fVirus, simp->szBuffer);
12
return
simp->nNo;
13
}
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))
下面这个例子来自网上,本来想自己写个,懒得写了,能说明问题就行:
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、例子
说得天花乱坠,嘿嘿,还是看两个实际的例子。
例子一:
这是一个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))
{
// "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X"
_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():
"""
创建一个全局唯一标识符
类似:E06093E2-699A-4BF2-A325-4F1EADB50E18
NewVersion
"""
try
:
# dll path
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
例子二:
这个例子是调用kernel32.dll中的createprocessA函数来启动一个记事本进程。
# -*- coding:utf-8 -*-
from
ctypes
import
*
# 定义_PROCESS_INFORMATION结构体
class
_PROCESS_INFORMATION(Structure):
_fields_
=
[(
'hProcess'
, c_void_p),
(
'hThread'
, c_void_p),
(
'dwProcessId'
, c_ulong),
(
'dwThreadId'
, c_ulong)]
# 定义_STARTUPINFO结构体
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
# 定义NORMAL_PRIORITY_CLASS
kernel32
=
windll.LoadLibrary(
"kernel32.dll"
)
# 加载kernel32.dll
CreateProcess
=
kernel32.CreateProcessA
# 获得CreateProcess函数地址
ReadProcessMemory
=
kernel32.ReadProcessMemory
# 获得ReadProcessMemory函数地址
WriteProcessMemory
=
kernel32.WriteProcessMemory
# 获得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://www.52sky.org/73004.html