1 ctypes简介
从Python2.5开始引入。
ctypes是Python的外部函数库。它提供了C兼容的数据类型,并且允许调用动态链接库/共享库中的函数。它可以将这些库包装起来给Python使用。
2 ctypes入门
本入门中的代码使用doctest确保可用。不过一些代码在linux/windows/mac os x中的行为可能略有差异,这在其doctest的注释中有所表示。
少数代码示例引用了ctypes的c_int类型。这个类型是32bit系统中c_long类型的别名。所以你在期待c_int而显示c_long时不必疑惑,他们是一样的。
2.1 载入动态链接库
ctypes导出了 cdll,在windows上还有 windll 和 oledll 对象用于载入动态链接库。
载入动态链接库可以直接存取其属性。 cdll 载入导出函数符合cdecl调用规范的库,而 windll 载入导出函数符合 stdcall 调用规范的库, oledll 也使用 stdcall 调用规范,并假设函数返回Windows的HRESULT错误码。错误码用于在出错时自动抛出WindowsError这个Python异常。
如下是Windows的例子,主意msvcrt是MS标准C库,包含了大部分标准C函数,并且使用cdecl调用规范:
>>> from ctypes import *
>>> print windll.kernel32
<WinDLL 'kernel32', handle ... at ...>
>>> print cdll.msvcrt
<CDLL 'msvcrt', handle ... at ...>
>>> libc=cdll.msvcrt
>>>
Windows通常使用".dll"作为动态链接库的扩展名。
Linux上需要指定包含扩展名的文件名来载入动态库,所以属性存取方式就失效了。你可以使用 LoadLibrary 方法,或者创建CDLL的实例来载入:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc==CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
2.2 从载入的动态链接库中访问函数
函数是作为dll对象的属性来存取的:
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print windll.kernel32.GetModuleHandleA
<_FuncPtr object at 0x...>
>>> print windll.kernel32.MyOwnFunction
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name,self)
AttributeError: function 'MyOwnFunction' not found
>>>
注意win32系统动态链接库,如kernel32和user32经常同时导出ANSI和UNICODE版本的函数。UNICODE版本的会在名字末尾加"W",而ANSI版本的加上"A"。Win32版本的 GetModuleHandle 函数,返回给定模块名的句柄,有如下C原型,还有一个宏用于暴露其中一个作为 GetModuleHandle ,依赖于UNICODE定义与否:
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll 并不会自动选择调用某个版本,所以你必须指定要调用的,传递的时候也要指定正确的字符串参数类型。
有时动态链接库导出函数并不是有效的Python标识符,例如 "??2@YAPAXI@Z" 。这种情况下,你必须使用getattr 获取函数:
>>> getattr(cdll.msvcrt,"??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
在Windows上,有些动态链接库导出函数不是用名字,而是用序号(ordinal)。这些函数通过索引存取:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name,self)
AttributeError: function ordinal 0 not found
>>>
2.3 调用函数
你可以像正常的Python函数一样调用这些函数。这里用 time() 函数示例,返回Unix epoch系统时间,和GetModuleHandleA() 函数,返回win32模块句柄。
这个例子调用函数时附带NULL指针(None作为NULL指针):
>>> print libc.time(None)
1150640792
>>> print hex(windll.kernel32.GetModuleHandleA(None))
0x1d000000
>>>
在调用函数时,如果使用了错误的参数数量和调用规范时,ctypes尝试保护调用。不幸的是该功能仅在Windows上有用。它通过检查函数返回栈来实现,所以尽管发生了错误,但是函数还是调用了:
>>> windll.kernel32.GetModuleHandleA()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with not enough argument (4 bytes missing)
>>> windll.kernel.GetModuleHandleA(0,0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with too many argument (4 bytes in excess)
这在你使用了错误的调用规范时同样会发生:
>>> cdll.kernel32.GetModuleHandleA(None) # doctest: +WINDOWS
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf("spam") # doctest: +WINDOWS
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
想要找到正确的调用规范,你必须查看C头文件或者函数的文档。
在Windows,ctypes使用win32结构异常处理,避免无保护的挂掉:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
WindowsError: exception: access violation reading 0x00000020
尽管如此,仍然有很多方法用ctypes挂掉Python,所以你必须很小心的使用。
None、整数、长整数、字节串和unicode字符串是可以作为本地Python对象直接传递给函数调用的。None是作为C的NULL指针,字 节串和unicode字符串作为内存块指针传递(char* 或 wchar_t*)。Python整数和长整数作为平台相关的C类型传递。
在调用更多的函数之前,必须了解关于ctypes数据类型的知识。
2.4 基本数据类型
ctypes定义了一系列基本C数据类型:
ctypes类型 |
C类型 |
Python类型 |
c_char |
char |
1个字符的字符串 |
c_wchar |
wchar_t |
1个字符的unicode字符串 |
c_byte |
char |
int/long |
c_ubyte |
unsigned char |
int/long |
c_short |
short |
int/long |
c_ushort |
unsigned short |
int/long |
c_int |
int |
int/long |
c_uint |
unsigned int |
int/long |
c_long |
long |
int/long |
c_ulong |
unsigned long |
int/long |
c_longlong |
__int64 或 long long |
int/long |
c_ulonglong |
unsigned __int64 或 unsigned long long |
int/long |
c_float |
float |
float |
c_double |
double |
float |
c_char_p |
char * (NUL结尾字符串) |
string或None |
c_wchar_p |
wchar_t * (NUL结尾字符串) |
unicode或None |
c_void_p |
void * |
int/long或None |