Python | 使用ctypes访问dll
问题内容:
我正在尝试访问Firefox Web浏览器随附的dll( nss3.dll
)中的某些功能。为了处理此任务,我在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安装路径加载它,假设可能存在依赖性。
>>> windll.LoadLibrary("F:\\Softwares\\Mozilla Firefox\\nss3.dll")
但我遇到了与上述相同的异常。
谢谢。
问题答案:
nss3.dll链接到以下DLL,它们都位于Firefox目录中:nssutil3.dll,plc4.dll,plds4.dll,nspr4.dll和mozcrt19.dll。系统库加载程序在进程的DLL搜索路径中查找这些文件,这些路径包括应用程序目录,系统目录,当前目录以及PATH
环境变量中列出的每个目录。
最简单的解决方案是将当前目录更改为DLL
Firefox目录。但是,这不是线程安全的,因此我一般不会依赖它。另一个选择是将Firefox目录附加到PATH
环境变量,这是我在该答案的原始版本中建议的内容。但是,这并不比修改当前目录好多少。
(与更新KB2533623 NT
6.0或更高版本)较新版本的Windows让DLL搜索路径在一个线程安全的方式通过更新SetDefaultDllDirectories
,AddDllDirectory
和RemoveDllDirectory
。但是这种方法将是最重要的。
在这种情况下,为了简化和与Windows的较旧版本兼容,只需LoadLibraryEx
用flag调用就足够了LOAD_WITH_ALTERED_SEARCH_PATH
。您需要使用绝对路径加载DLL,否则行为是不确定的。为了方便起见,我们可以继承ctypes.CDLL
和ctypes.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'