蟒蛇|使用 ctypes 访问 dll [英] Python | accessing dll using ctypes
问题描述
我正在尝试访问 Firefox 网络浏览器附带的 dll (nss3.dll) 中的某些功能.为了处理这个任务,我在 Python 中使用了 ctypes.问题是它在初始点失败,即在将 dll 加载到内存中时.
这是我必须这样做的代码片段.
<预><代码>>>>从 ctypes 导入 *>>>windll.LoadLibrary("E:\nss3.dll")我得到的例外是
回溯(最近一次调用最后一次):文件<pyshell#2>",第 1 行,在 <module> 中windll.LoadLibrary("E:\nss3.dll")LoadLibrary 中的文件C:Python26libctypes\__init__.py",第 431 行返回 self._dlltype(name)文件C:Python26libctypes\__init__.py",第 353 行,在 __init__ 中self._handle = _dlopen(self._name, mode)WindowsError: [错误 126] 找不到指定的模块
假设可能存在依赖关系,我还尝试从 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
环境变量,这是我在本答案的原始版本中建议的.但是,这并不比修改当前目录好多少.
较新版本的 Windows(NT 6.0+ 更新 KB2533623)允许通过 SetDefaultDllDirectories
、AddDllDirectory
和 SetDefaultDllDirectories
以线程安全的方式更新 DLL 搜索路径代码>RemoveDllDirectory.但这种方法在这里是过分的.
在这种情况下,为了简单起见并与旧版本的 Windows 兼容,只需使用标志 LOAD_WITH_ALTERED_SEARCH_PATH
调用 LoadLibraryEx
就足够了.您需要使用绝对路径加载 DLL,否则行为未定义.为方便起见,我们可以将 ctypes.CDLL
和 ctypes.WinDLL
子类化以调用 LoadLibraryEx
而不是 LoadLibrary
.
导入操作系统导入 ctypes如果 os.name == 'nt':从 ctypes 导入 wintypeskernel32 = ctypes.WinDLL('kernel32', use_last_error=True)def check_bool(result, func, args):如果不是结果:引发 ctypes.WinError(ctypes.get_last_error())返回参数kernel32.LoadLibraryExW.errcheck = check_boolkernel32.LoadLibraryExW.restype = wintypes.HMODULEkernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR,wintypes.HANDLE,wintypes.DWORD)类 CDLLEx(ctypes.CDLL):def __init__(self, name, mode=0, handle=None,use_errno=True,use_last_error=False):如果 os.name == 'nt' 并且句柄为 None:handle = kernel32.LoadLibraryExW(name, None, mode)super(CDLLEx, self).__init__(name, mode, handle,use_errno, use_last_error)类 WinDLLEx(ctypes.WinDLL):def __init__(self, name, mode=0, handle=None,use_errno=False,use_last_error=True):如果 os.name == 'nt' 并且句柄为 None:handle = kernel32.LoadLibraryExW(name, None, mode)super(WinDLLEx, self).__init__(name, mode, handle,use_errno, use_last_error)
以下是所有可用的 LoadLibraryEx
标志:
DONT_RESOLVE_DLL_REFERENCES = 0x00000001LOAD_LIBRARY_AS_DATAFILE = 0x00000002LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010 # NT 6.1LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 # NT 6.0LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040 # NT 6.0# 这些不能与 LOAD_WITH_ALTERED_SEARCH_PATH 结合使用.# 为 NT 6.0 & 安装更新 KB25336236.1.LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000
例如:
firefox_path = r'F:SoftwaresMozilla 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 基本 ECC'
I'm trying to access some functions in a dll (nss3.dll) that ships with Firefox web browser. To handle this task I have used ctypes in Python. The problem is that it fails at the initial point which is when loading the dll in to the memory.
This is the code snippet that I have to do so.
>>> from ctypes import *
>>> windll.LoadLibrary("E:\nss3.dll")
The exception I'm getting is
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
windll.LoadLibrary("E:\nss3.dll")
File "C:Python26libctypes\__init__.py", line 431, in LoadLibrary
return self._dlltype(name)
File "C:Python26libctypes\__init__.py", line 353, in __init__
self._handle = _dlopen(self._name, mode)
WindowsError: [Error 126] The specified module could not be found
I also tried loading it from the Firefox installation path assuming that there maybe dependencies.
>>> windll.LoadLibrary("F:\Softwares\Mozilla Firefox\nss3.dll")
But I'm getting the same exception as mentioned above.
Thanks.
nss3.dll is linked to the following DLLs, which are all located in the Firefox directory: nssutil3.dll, plc4.dll, plds4.dll, nspr4.dll, and mozcrt19.dll. The system library loader looks for these files in the DLL search path of the process, which includes the application directory, system directories, the current directory, and each of the directories listed in the PATH
environment variable.
The simplest solution is to change the current directory to the DLL Firefox directory. However, that's not thread safe, so I wouldn't rely on it in general. Another option is to append the Firefox directory to the PATH
environment variable, which is what I suggested in my original version of this answer. However, that's not much better than modifying the current directory.
Newer versions of Windows (NT 6.0+ with update KB2533623) allow the DLL search path to be updated in a thread-safe manner via SetDefaultDllDirectories
, AddDllDirectory
, and RemoveDllDirectory
. But that approach would be over the top here.
In this case, for the sake of both simplicity and compatibility with older versions of Windows, it suffices to call LoadLibraryEx
with the flag LOAD_WITH_ALTERED_SEARCH_PATH
. You need to load the DLL using an absolute path, else the behavior is undefined. For convenience we can subclass ctypes.CDLL
and ctypes.WinDLL
to call LoadLibraryEx
instead of 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)
Here are all of the available LoadLibraryEx
flags:
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
For example:
firefox_path = r'F:SoftwaresMozilla 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 访问 dll的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!