【问题标题】:Python Ctypes: Loading C++ DLL - "module not found" or "function 'fn_123' not found"Python Ctypes:加载 C++ DLL - “找不到模块”或“找不到函数‘fn_123’”
【发布时间】:2018-05-23 21:07:17
【问题描述】:

我正在尝试从已经编译的(不是由我编译的)C++ DLL 文件中加载并调用函数。

我面临两个有问题的场景:

  1. 使用 CDLL() 加载 DLL:

    from ctypes import *
    __path_aa_Fn_dll__ = r'C:\user\aacad_2017\aa_Functions.dll' # DLL file path
    aa_Fn_dll = CDLL(__path_aa_Fn_dll__) # load DLL
    

    这给了我找不到模块 OSError:

    Traceback (most recent call last):
    File "C:/user/aacad_2017/cadClass.py", line 189, in <module>
    aa_Fn_dll = CDLL(__path_aa_Fn_dll__)
    File "C:\Users\user\AppData\Local\Continuum\anaconda3\lib\ctypes\__init__.py", line 348, in __init__
    self._handle = _dlopen(self._name, mode)
    OSError: [WinError 126] The specified module could not be found
    
  2. 使用 win32api.LoadLibraryEx() 然后 CDLL() 加载相同的 DLL

    aa_Fn_dll_handle = win32api.LoadLibraryEx(__path_aa_Fn_dll__, 0, win32con.LOAD_LIBRARY_AS_DATAFILE) # get DLL handle
    aa_Fn_dll = CDLL(__path_aa_Fn_dll__, handle=aa_Fn_dll_handle) # load DLL
    

    能够加载DLL。

    但是,当尝试调用此 DLL 中的函数时,会引发以下 AttributeError:

    aa_Fn_dll.aaBIN2iv.restype = c_double # set function return type
    aa_Fn_dll.aaBIN2iv.argtypes = [c_double, c_double, c_double, c_double, c_double, c_double, c_double, c_char_p] # set function argument types
    aa_Fn_dll.aaBIN2iv(10, 10, 10, 10, 10, 10, 10, b"C") # call DLL function
    
    
    Traceback (most recent call last):
    File "C:/user/aacadMatlabWrap_2017_new_test/cadClass.py", line 196, in <module>
    aa_Fn_dll.aaBIN2iv.restype = c_double
    File "C:\Users\user\AppData\Local\Continuum\anaconda3\lib\ctypes\__init__.py", line 361, in __getattr__
    func = self.__getitem__(name)
    File "C:\Users\user\AppData\Local\Continuum\anaconda3\lib\ctypes\__init__.py", line 366, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
    AttributeError: function 'aaBIN2iv' not found
    

经过广泛的在线研究和查阅 Ctypes 文档,我尝试了以下历史解决方案:

  • 使用 Dependency Walker 检查依赖关系
  • 确保所有 DLL 依赖项都放在同一目录中
  • 将 DLL 目录添加到系统 PATH
  • 确保将 extern "C" 添加到 C++ 头文件中以防止名称错误
  • 在 C++ 头文件中使用关键字EXPORTED_FUNCTION
  • 在加载 DLL 之前将 Python 中的当前工作目录设置为 DLL 文件目录的目录

没有成功...

我还在 Microsoft Visual Studio 2010 中以调试和发布模式编译了我自己的 SEPARATE 64 位 C++ DLL 文件(完全独立于前面提到的 aa_Functions.dll 文件),其中包含测试函数。

这个测试DLL能够被加载,并且它的函数被成功调用,如下:

__path_test_dll__ = r'C:\user\dllTest\x64\Release\dllTest.dll' # test DLL file path
test_dll = CDLL(__path_test_dll__) # load test DLL
test_dll.fn4.restype = c_void_p # set test DLL function return type
test_dll.fn4.argtypes = [c_double, c_double, c_double, c_char_p, c_bool] # set test DLL function argument types
test_dll.fn4(10, 12, 1, b"Pay", True) # make test DLL function call

使用预期的控制台输出:

Min_P * Rate == $10
Pay: 1 Min_P: 10 Max_P: 12 Iteration #: 1
Min_P * Rate == $11
Pay: 0 Min_P: 11 Max_P: 12 Iteration #: 2
Min_P * Rate == $12
Pay: 1 Min_P: 12 Max_P: 12 Iteration #: 3

Process finished with exit code 0

过去几天我一直在研究这个问题,如果能指出正确的方向,我将不胜感激。

【问题讨论】:

  • C++ 目标文件/库的 ABI 通常在编译器供应商/版本之间不稳定。您确定 dll 是使用与您正在运行的试图加载 dll 的程序完全相同的编译器编译的吗?如果没有,您可能会遇到一些有趣的麻烦。
  • 将头文件更改为具有extern "C"EXPORTED_FUNCTION 等,而不重新编译DLL 将不起作用。如果缺少这些,则函数可能会导出为 C++ 损坏的名称而不是 C 名称。使用dumpbin /EXPORTS your.dll 查看名称的样子。 LOAD_LIBRARY_AS_DATAFILE 也不会从 DLL IIRC 加载函数表。
  • 它可以由 pywin32 而不是由 ctypes 加载的事实没有意义。你肯定做错了(也许没有使用相同的路径)?那么您的 Python(还有 ctypespywin32)版本是什么?别人用什么工具编译.dll?第二种情况,根据您加载 dll 的方式,找不到符号是正常的。因此,使用加载 dll 的测试 dll(您编译的所有内容)一切正常吗?加载外部编译的 dll 的测试 dll 怎么样?
  • @MarkTolonen DLL 确实是由其他人编译的,头文件中已经有extern "C"EXPORTED_FUNCTION。使用 dumpbin /exports aa_Function.dll 我可以看到函数名称,它与 Dependency Walker 中显示的名称相匹配。我在 Python 中使用相同的函数名称在 Python 中进行函数调用 - 不成功。你关于LOAD_LIBRARY_AS_DATAFILE 不加载DLL 函数表的观点很有趣。在文档中找不到有关此加载方法的任何内容。这在加载 DLLS 时是有意义的,但无法调用函数。
  • 我正在使用 Python 3.6.4 64 位,(在 win32 上使用 MSC v.1900 构建,即 Visual Studio 2013 版本 14.0)通过模块 Ctypes 尝试加载使用 Visual Studio 编译的 DLL 2013. 编译 DLL 的人说编译器版本的这种差异不应该引起任何冲突......不确定下一步应该是什么......

标签: python c++ dll wrapper ctypes


【解决方案1】:

我的具体问题的解决方案是:

  1. 确保正确安装 MATLAB
  2. 确保将依赖的 MATLAB DLL (libmx.dll) 目录添加到 PATH,并且可以在加载实际 DLL 之前使用 ctypes 加载

事后看来,这是一个非常直接的问题 - 缺少 DLL 依赖项。但找到特定缺失的 DLL 可能是一项挑战。

【讨论】:

    猜你喜欢
    • 2020-07-30
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    • 2019-05-15
    相关资源
    最近更新 更多