2011-09-28 166 views
12

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

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

>>> 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的安装路径加载它假定有可能依赖。

​​

但是我收到了与上面提到的相同的异常。

谢谢。

+2

你确定它是一个Windows DLL而不是C DLL吗?你有没有尝试ctypes库中的'cdll.LoadLibrary'? –

+0

是的,我完全忘了那个。 – Switch

回答

15

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

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

较新版本的Windows(具有更新KB2533623的NT 6.0+)允许通过SetDefaultDllDirectories,AddDllDirectoryRemoveDllDirectory以线程安全的方式更新DLL搜索路径。但是,这种方法将在这里成为顶峰。

在这种情况下,为了简化和兼容旧版本的Windows,只需拨打LoadLibraryEx即可,标记为LOAD_WITH_ALTERED_SEARCH_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' 
9

注意,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 

命令提示:

用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 
+2

我一直在努力获得在C++和python之间运行的基本通信,在2天的时间范围内,现在需要大约12小时。谢谢你的回答,因为它终究是我在寻找的。 –

+1

@ZoranPavlovic,我希望你已经学会了,当你的代码根本不使用它时,不会链接python33.lib。您的代码最终将被加载到Python进程中,不需要任何编译时链接到Python DLL。这将是奇怪的,几乎完全破坏了创建DLL的要点。 – eryksun

+0

尝试过,它不起作用。我用你提供的确切代码创建了2种使用dev-C++和visual studio的方式,它不起作用。我收到错误:指定的模块找不到。 python中的载入行发生错误。 – Brana

5

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

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

我做过

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

如果您处于单线程进程中,或者您只在程序启动时加载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'。 – eryksun