Python | 使用ctypes访问dll

Swi*_*tch 16 python windows dll firefox ctypes

我正在尝试访问Firefox Web浏览器附带的dll(nss3.dll)中的某些功能.为了处理这个任务,我在Python中使用了ctypes.问题是它在将dll加载到内存时的初始点失败.

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

>>> from ctypes import *
>>> windll.LoadLibrary("E:\\nss3.dll")
Run Code Online (Sandbox Code Playgroud)

我得到的例外是

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
Run Code Online (Sandbox Code Playgroud)

我也尝试从Firefox安装路径加载它,假设可能存在依赖关系.

>>> windll.LoadLibrary("F:\\Softwares\\Mozilla Firefox\\nss3.dll")
Run Code Online (Sandbox Code Playgroud)

但是我得到了与上面提到的相同的例外.

谢谢.

Ery*_*Sun 20

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

最简单的解决方案是将当前目录更改为DLL Firefox目录.但是,这不是线程安全的,所以我一般不会依赖它.另一个选择是将Firefox目录附加到PATH环境变量,这是我在本答案的原始版本中建议的.但是,这并不比修改当前目录好多少.

新版本的Windows(NT 6.0以上版本与更新KB2533623)允许DLL搜索路径在一个线程安全的方式通过更新SetDefaultDllDirectories,AddDllDirectoryRemoveDllDirectory.但这种方法在这里将超过顶部.

在这种情况下,为了简单和与旧版本Windows的兼容性,只需LoadLibraryEx使用标志调用即可LOAD_WITH_ALTERED_SEARCH_PATH.您需要使用绝对路径加载DLL,否则行为未定义.为方便起见,我们可以子类化ctypes.CDLL而不是ctypes.WinDLL调用.LoadLibraryExLoadLibrary

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)
Run Code Online (Sandbox Code Playgroud)

以下是所有可用的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
Run Code Online (Sandbox Code Playgroud)

例如:

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'
Run Code Online (Sandbox Code Playgroud)


Rez*_*imi 9

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

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

#include <math.h>

#ifdef __cplusplus
extern "C" {
#endif

__declspec(dllexport) double _sin(double x)
{
     return sin(x)
}

#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)

管理员身份验证的命令提示

有了C源:

C:\Windows\system32>cl /LD "your_source_path\dll.c" /I "c:\Python33 \include" "c:\Python33\libs\python33.lib" /link/out:dll.dll
Run Code Online (Sandbox Code Playgroud)

使用C++源代码:

C:\Windows\system32>cl /LD "your_source_path\dll.cpp" /I "c:\Python33 \include" "c:\Python33\libs\python33.lib" /link/out:dll.dll
Run Code Online (Sandbox Code Playgroud)

编译器生成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
Run Code Online (Sandbox Code Playgroud)

你的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
Run Code Online (Sandbox Code Playgroud)

  • 我已经努力在C++和python之间运行基本的通信,在2天的时间内现在大约12小时.感谢您的回答,因为它最终是我所寻求的. (2认同)

lib*_*lib 5

我只是遇到了与ctypes.CDLL类似的问题,我让它将当前目录更改为库目录并仅按名称加载库(我想将目录放在系统路径中也会起作用)所以,而不是

CDLL('C:/library/path/library.dll')
Run Code Online (Sandbox Code Playgroud)

我做到了

os.chdir('C:/library/path')
CDLL('library')
Run Code Online (Sandbox Code Playgroud)

  • 如果您处于单线程进程中或者仅在程序启动时加载DLL,则更改当前目录是可以的.通常,更改工作目录不是线程安全的.对于更新的Windows Vista +(即安装了KB2533623),您可以调用[`AddDllDirectory`](https://msdn.microsoft.com/en-us/library/hh310513)和[`SetDefaultDllDirectories`](https:// msdn.microsoft.com/en-us/library/hh310515).如果`kernel32.AddDllDirectory`不存在,则返回扩展`PATH`. (2认同)