内容:.加载动态链接库.从已加载的dll中引用函数.调用函数1.基本的数据类型.调用函数2.用自己的数据类型调用函数.确认需要的参数类型(函数原型).返回值.传递指针.结构和联合.结构或联合的对齐方式和字节的顺序.结构和联合中的位.数组.指针.类型转换.未完成的类型.回调函数.访问dlls导出的值.可变长度的数据类型.bugs 将要做的和没有做的事情注意:本文中的代码例子使用doctest确保他们能够实际工作。
一些代码例子在linux和windows以及苹果机上执行有一定的差别注意:一些代码引用了ctypes的c_int类型。
它是c_long在32位机子上的别名,你不应该变得迷惑,如果你期望的是c_int类型,实事上打印的是c_long,它们实事上是相同的类型。
加载动态链接库ctypes加载动态链接库,导出cdll和在windows上同样也导出windll和oledll对象。
加载动态链接库后,你可以像使用对象的属性一样使用它们。
cdll加载使用标准的cdecl调用约定的链接库,而windll库使用stdcall调用约定,oledll也使用stdcall调用约定,同时确保函数返回一个windows HRESULT错误代码。
这错误代码自动的升为WindowsError Python exceptions,当这些函数调用失败时。
这有一些windows例子,msvcrt是微软的c标准库,包含大部分的标准c函数,同时使用cdecl调用约定。
注:cdecl和stdcall的区别请见/log-20.html>>> from ctypes import *>>> print windll.kernel32 # doctest: +WINDOWS<WinDLL 'kernel32', handle ... at ...>>>> print cdll.msvcrt # doctest: +WINDOWS<CDLL 'msvcrt', handle ... at ...>>>> libc = cdll.msvcrt # doctest: +WINDOWS>>>windows自动添加常用的.dll文件后缀名在linux上,需要使用包含扩展名的文件名来加载一个库,因此属性操作不能正常使用。
或者使用dll加载器的LoadLibrary方法,或者通过CDLL构造函数创建一个CDLL的一个实例>>> cdll.LoadLibrary("libc.so.6") # doctest: +LINUX<CDLL 'libc.so.6', handle ... at ...>>>> libc = CDLL("libc.so.6") # doctest: +LINUX>>> libc # doctest: +LINUX<CDLL 'libc.so.6', handle ... at ...>>>>加载dll使用其中函数使用函数就像对象的属性>>> from ctypes import *>>> libc.printf<_FuncPtr object at 0x...>>>> print windll.kernel32.GetModuleHandleA # doctest: +WINDOWS<_FuncPtr object at 0x...>>>> print windll.kernel32.MyOwnFunction # doctest: +WINDOWSTraceback (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>>>00注意win32系统dll像kernel32和user32大部分导出ANSI和UNICODE版本函数,UNICODE 版本以一个W结尾导出而ANSI版本则以一个A结尾导出的。
win32 GetModuleHandle函数,返回一个指定的module 名字的module句柄下面c原型,GetModuleHandle的macro,依赖于它是不是UNICODE。
/* ANSI version */HMODULE GetModuleHandleA(LPCSTR lpModuleName);/* UNICODE version */HMODULE GetModuleHandleW(LPCWSTR lpModuleName);windll不会神奇的自已去选择一个,你必须显式确认GetModuleHandleA或者GetModuleHandleW去使用它们去处理一般字符串或unicode字符串。
有时候,dll导出函数名,python无法识别,像"??2@YAPAXI@Z". 在这情况下,你必须使用getattr去使用这些函数>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") # doctest: +WINDOWS<_FuncPtr object at 0x...>>>>在windows上,一些dllS不是导出函数名,而是以顺序,这些函数可以将这些数字当作dll 对象的索引来使用这些函数。
>>> cdll.kernel32[1] # doctest: +WINDOWS<_FuncPtr object at 0x...>>>> cdll.kernel32[0] # doctest: +WINDOWSTraceback (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>>>调用函数你可调用这些函数,像其它的python函数一样,下面的例子使用time()函数,它以秒为单位返回从unix新纪元的系统时间,GetModuleHandleA()函数,返回一个win32模块句柄。
下面的例子用空指针调用函数(None作为空指针)>>> print libc.time(None) # doctest: +SKIP1150640792>>> print hex(windll.kernel32.GetModuleHandleA(None)) # doctest: +WINDOWS0x1d000000>>>ctypes尝试阻止你以错误的参数数量或调用约定来调用函数。
不幸的是,这些仅仅能在windows上工作它在函数返回后不会去检查这栈,尽管在调用函数后有错误发生。
>>> windll.kernel32.GetModuleHandleA() # doctest: +WINDOWSTraceback (most recent call last):File "<stdin>", line 1, in ?ValueError: Procedure probably called with not enough arguments (4 bytes missing)>>> windll.kernel32.GetModuleHandleA(0, 0) # doctest: +WINDOWSTraceback (most recent call last):File "<stdin>", line 1, in ?ValueError: Procedure probably called with too many arguments (4 bytes in excess)>>>产生了同样的exception,当你用cdecl调用约定去使用一个stdcall函数,反之亦然。
>>> cdll.kernel32.GetModuleHandleA(None) # doctest: +WINDOWSTraceback (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: +WINDOWSTraceback (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结构exception处理一般的保护错误阻止crashes,当调用无效的参数值>>> windll.kernel32.GetModuleHandleA(32) # doctest: +WINDOWSTraceback (most recent call last):File "<stdin>", line 1, in ?WindowsError: exception: access violation reading 0x00000020>>>然而有很多种可能性会发生crash,你使用python的ctypes,你应该足够的小心。