Python | 访问DLL使用ctypes

我试图访问Firefox浏览器附带的DLL( nss3.dll )中的一些function。 为了处理这个任务,我在Python中使用了ctypes。 问题是,它在加载到内存中的DLL的初始点失败。

这是我必须这样做的代码片段。

>>> from ctypes import * >>> windll.LoadLibrary("E:\\nss3.dll") 

我得到的例外是

 Traceback (most recent call last): File "<pyshell#2>", line 1, in <module> windll.LoadLibrary("E:\\nss3.dll") File "C:\Python26\lib\ctypes\__init__.py", line 431, in LoadLibrary return self._dlltype(name) File "C:\Python26\lib\ctypes\__init__.py", line 353, in __init__ self._handle = _dlopen(self._name, mode) WindowsError: [Error 126] The specified module could not be found 

我也尝试从Firefox安装path加载它假设可能有依赖关系。

 >>> windll.LoadLibrary("F:\\Softwares\\Mozilla Firefox\\nss3.dll") 

但是我得到了上面提到的exception。

谢谢。

nss3.dll链接到以下DLL,它们都位于Firefox目录中:nssutil3.dll,plc4.dll,plds4.dll,nspr4.dll和mozcrt19.dll。 系统库加载程序会在进程的DLLsearchpath中查找这些文件,其中包括应用程序目录,系统目录,当前目录以及PATH环境variables中列出的每个目录。

最简单的解决scheme是将当前目录更改为DLL Firefox目录。 但是,这不是线程安全的,所以我不会依赖它。 另一个select是将Firefox目录附加到PATH环境variables,这是我在这个答案的原始版本中所build议的。 但是,这并不比修改当前目录好多less。

较新版本的Windows(具有更新KB2533623的NT 6.0+)允许通过SetDefaultDllDirectoriesAddDllDirectoryRemoveDllDirectory以线程安全方式更新DLLsearchpath。 但是,这种方法将在这里顶部。

在这种情况下,为了简化和兼容旧版本的Windows,只需调用LoadLibraryEx标志LOAD_WITH_ALTERED_SEARCH_PATH 。 您需要使用绝对path加载DLL,否则行为是未定义的。 为了方便起见,我们可以ctypes.CDLLctypes.WinDLL来调用LoadLibraryEx而不是LoadLibrary

 import os import ctypes if os.name == 'nt': from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) def check_bool(result, func, args): if not result: raise ctypes.WinError(ctypes.get_last_error()) return args kernel32.LoadLibraryExW.errcheck = check_bool kernel32.LoadLibraryExW.restype = wintypes.HMODULE kernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR, wintypes.HANDLE, wintypes.DWORD) class CDLLEx(ctypes.CDLL): def __init__(self, name, mode=0, handle=None, use_errno=True, use_last_error=False): if os.name == 'nt' and handle is None: handle = kernel32.LoadLibraryExW(name, None, mode) super(CDLLEx, self).__init__(name, mode, handle, use_errno, use_last_error) class WinDLLEx(ctypes.WinDLL): def __init__(self, name, mode=0, handle=None, use_errno=False, use_last_error=True): if os.name == 'nt' and handle is None: handle = kernel32.LoadLibraryExW(name, None, mode) super(WinDLLEx, self).__init__(name, mode, handle, use_errno, use_last_error) 

以下是所有可用的LoadLibraryEx标志:

 DONT_RESOLVE_DLL_REFERENCES = 0x00000001 LOAD_LIBRARY_AS_DATAFILE = 0x00000002 LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008 LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010 # NT 6.1 LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 # NT 6.0 LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040 # NT 6.0 # These cannot be combined with LOAD_WITH_ALTERED_SEARCH_PATH. # Install update KB2533623 for NT 6.0 & 6.1. LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100 LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200 LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400 LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 

例如:

 firefox_path = r'F:\Softwares\Mozilla Firefox' nss3 = CDLLEx(os.path.join(firefox_path, 'nss3.dll'), LOAD_WITH_ALTERED_SEARCH_PATH) nss3.NSS_GetVersion.restype = c_char_p >>> nss3.NSS_GetVersion() '3.13.5.0 Basic ECC' 

请注意,ctypes模块适用于C扩展; 如果你想用C ++编写代码,你可以这样做(C代码是一样的):

你的dll.c源代码:(你可以使用C ++代码,扩展名为.cpp,没有任何问题)

 #include <math.h> #ifdef __cplusplus extern "C" { #endif __declspec(dllexport) double _sin(double x) { return sin(x) } #ifdef __cplusplus } #endif 

带pipe理员身份validation的命令

用C源码:

 C:\Windows\system32>cl /LD "your_source_path\dll.c" /I "c:\Python33 \include" "c:\Python33\libs\python33.lib" /link/out:dll.dll 

用C ++来源:

 C:\Windows\system32>cl /LD "your_source_path\dll.cpp" /I "c:\Python33 \include" "c:\Python33\libs\python33.lib" /link/out:dll.dll 

编译器生成DLL文件:

 Microsoft (R) C/C++ Optimizing Compiler Version 17.00.50727.1 for x86 Copyright (C) Microsoft Corporation. All rights reserved. dll.c // or dll.cpp Microsoft (R) Incremental Linker Version 11.00.50727.1 Copyright (C) Microsoft Corporation. All rights reserved. /out:dll.dll /dll /implib:dll.lib /out:dll.dll dll.obj c:\Python33\libs\python33.lib Creating library dll.lib and object dll.exp 

你的Python模块:

 import ctypes dll = ctypes.CDLL('your_dll_path') dll._sin.argtypes = [ctypes.c_double] dll._sin.restype = ctypes.c_double print(dll._sin(34)) # return 0.5290826861200238 

我只是与ctypes.CDLL有类似的问题,我得到它的工作改变当前目录到库目录,并加载只有名称的库(我想把目录在系统path也可以)所以,而不是

 CDLL('C:/library/path/library.dll') 

我做了

 os.chdir('C:/library/path') CDLL('library')