加载动态链接库¶
有很多方式可以将动态链接库加载到 Python 进程。其中之一是实例化以下类的其中一个:
classctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)¶
此类的实例即已加载的动态链接库。库中的函数使用标准 C 调用约定,并假定返回 int 。
在 Windows 上创建 CDLL 实例可能会失败,即使 DLL 名称确实存在。 当某个被加载 DLL 所依赖的 DLL 未找到时,将引发 OSError 错误并附带消息 "[WinError 126] The specified module could not be found". 此错误消息不包含缺失 DLL 的名称,因为 Windows API 并不会返回此类信息,这使得此错误难以诊断。 要解决此错误并确定是哪一个 DLL 未找到,你需要找出所依赖的 DLL 列表并使用 Windows 调试与跟踪工具确定是哪一个未找到。
参见
Microsoft DUMPBIN 工具 -- 一个用于查找 DLL 依赖的工具。
classctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)¶
仅 Windows : 此类的实例即加载好的动态链接库,其中的函数使用 stdcall 调用约定,并且假定返回 windows 指定的 HRESULT 返回码。 HRESULT 的值包含的信息说明函数调用成功还是失败,以及额外错误码。 如果返回值表示失败,会自动抛出 OSError 异常。
在 3.3 版更改:以前是引发 WindowsError。
classctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)¶
仅 Windows: 此类的实例即加载好的动态链接库,其中的函数使用 stdcall 调用约定,并假定默认返回 int 。
在 Windows CE 上,只能使用 stdcall 调用约定,为了方便, WinDLL 和 OleDLL 在这个平台上都使用标准调用约定。
调用动态库导出的函数之前,Python会释放 global interpreter lock ,并在调用后重新获取。
classctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)¶
这个类实例的行为与 CDLL 类似,只不过 不会 在调用函数的时候释放 GIL 锁,且调用结束后会检查 Python 错误码。 如果错误码被设置,会抛出一个 Python 异常。
所以,它只在直接调用 Python C 接口函数的时候有用。
通过使用至少一个参数(共享库的路径名)调用它们,可以实例化所有这些类。也可以传入一个已加载的动态链接库作为 handler 参数,其他情况会调用系统底层的 dlopen 或 LoadLibrary 函数将库加载到进程,并获取其句柄。
mode 可以指定库加载方式。详情请参见 mode ,在 posix 系统上, 总是会加上 RTLD_NOW ,且无法配置。
use_errno 参数如果设置为 true,可以启用ctypes的机制,通过一种安全的方法获取系统的 errno 错误码。 ctypes 维护了一个线程局部变量,它是系统 errno 的一份拷贝;如果调用了使用 use_errno=True 创建的外部函数, errno 的值会与 ctypes 自己拷贝的那一份进行交换,函数执行完后立即再交换一次。
The function ctypes.get_errno() returns the value of the ctypes private
copy, and the function ctypes.set_errno() changes the ctypes private copy
to a new value and returns the former value.
use_last_error 参数如果设置为 true,可以在 Windows 上启用相同的策略,它是通过 Windows API 函数 GetLastError() 和 SetLastError() 管理的。 ctypes.get_last_error() 和 ctypes.set_last_error() 可用于获取和设置 ctypes 自己维护的 windows 错误码拷贝。
winmode 参数用于在 Windows 平台上指定库的加载方式( 因为 mode 会被忽略)。他接受任何与 Win32 API 的 LoadLibraryEx 的标志兼容的值作为参数。省略时,默认设置使用最安全的DLL加载的标志,以避免DLL劫持等问题。传入 DLL 的全路径是保证正确加载库及其依赖最安全的方法。
在 3.8 版更改:增加了 winmode 参数。
ctypes.RTLD_GLOBAL
用于 mode 参数的标识值。在此标识不可用的系统上,它被定义为整数0。
ctypes.RTLD_LOCAL
Flag to use as mode parameter. On platforms where this is not available, it
is the same as RTLD_GLOBAL.
ctypes.DEFAULT_MODE
加载动态链接库的默认模式。在 OSX 10.3 上,它是 RTLD_GLOBAL ,其余系统上是 RTLD_LOCAL 。
这些类的实例没有共用方法。动态链接库的导出函数可以通过属性或者数组下标的方式访问。注意,通过属性的方式访问会缓存这个函数,因而每次访问它时返回的都是同一个对象。另一方面,通过数组下标访问,每次都会返回一个新的对象:
>>>from ctypes import CDLL
>>>libc = CDLL("libc.so.6") # On Linux
>>>libc.time == libc.time
True
>>>libc['time'] == libc['time']
False
还有下面这些属性可用,他们的名称以下划线开头,以避免和导出函数重名:
PyDLL._handle¶
用于访问库的系统句柄。
PyDLL._name¶
传入构造函数的库名称。
共享库也可以通用使用一个预制对象来加载,这种对象是 LibraryLoader 类的实例,具体做法或是通过调用 LoadLibrary() 方法,或是通过将库作为加载实例的属性来提取。
classctypes.LibraryLoader(dlltype)¶
加载共享库的类。 dlltype 应当为 CDLL, PyDLL, WinDLL 或 OleDLL 类型之一。
__getattr__() 具有特殊的行为:它允许通过将一个共享库作为库加载器实例的属性进行访问来加载它。 加载结果将被缓存,因此重复的属性访问每次都会返回相同的库。
LoadLibrary(name)¶
加载一个共享库到进程中并将其返回。 此方法总是返回一个新的库实例。
可用的预制库加载器有如下这些:
ctypes.cdll
创建 CDLL 实例。
ctypes.windll
仅Windows中: 创建 WinDLL 实例.
ctypes.oledll
仅Windows中: 创建 OleDLL 实例。
ctypes.pydll
创建 PyDLL 实例。
要直接访问 C Python api,可以使用一个现成的 Python 共享库对象:
ctypes.pythonapi
一个 PyDLL 的实例,它将 Python C API 函数作为属性公开。 请注意所有这些函数都应返回 C int,当然这也不是绝对的,因此你必须分配正确的 restype 属性以使用这些函数。
引发一个 审计事件 ctypes.dlopen,附带参数 name。
引发一个审计事件 ctypes.dlsym,附带参数 library, name。
引发一个审计事件 ctypes.dlsym/handle,附带参数 handle, name。