【发布时间】:2018-05-23 21:07:17
【问题描述】:
我正在尝试从已经编译的(不是由我编译的)C++ DLL 文件中加载并调用函数。
我面临两个有问题的场景:
-
使用 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 -
使用 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(还有 ctypes 和 pywin32)版本是什么?别人用什么工具编译.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