在Python中加载具有依赖项的DLL

8
我有一个名为 proj1.dll 的文件,它依赖于另一个DLL文件 proj2.dll 。我使用VS2013编译 proj1.dll 时,编译器输出了导入库,用于编译 proj2.dll 。我还导出了我想要使用的公共函数。现在我有两个符合“cdll”标准的独立DLL文件。
我想在Python中使用 proj1.dll ,但我遇到了以下问题:
import ctypes

# Crashes saying no entry point for "some_func" in proj2.dll
ctypes.cdll.LoadLibrary("C:\myfolder\proj1.dll")

ctypes.cdll.LoadLibrary("C:\myfolder\proj2.dll") # Loads fine
ctypes.cdll.LoadLibrary("C:\myfolder\proj1.dll") # Loads fine if proj2 is loaded first

当我将proj2作为静态库构建并在proj1中链接到它时,从Python调用此DLL以前是有效的。这两个DLL文件存在于同一个文件夹中。我甚至尝试将该文件夹的路径添加到我的PATH环境变量中,以查看是否存在路径问题,但没有任何改变。

我曾认为Windows会加载proj1.dll,然后加载dll的依赖项。我错了吗?调用者(Python)是否必须先加载依赖的DLL文件?有人知道为什么会发生这种情况吗?


你的路径中是否包含 myfolder?你可能需要检查你的 proj1.dll 来确认它是否包含依赖项。https://dev59.com/uGs05IYBdhLWcg3wNPO7 - user590028
Python的版本是多少?出现了什么错误? - CristiFati
@CristiFati 我不确定 OP 是否还在回答你的问题。我开了悬赏是因为我有一个非常类似的问题 (https://stackoverflow.com/questions/61717476/loading-python-module-depending-on-dll-not-next-to-it),我认为对这个问题的更完整的回答也会回答我的问题 (如果这里有一个好的答案,我会将我的问题关闭为重复的)。请随意查看其他问题并在那里提问,也许我会反过来做同样的事情。 - Holt
@Holt:所以,这是一个不同于指向问题的问题。 - CristiFati
3个回答

5
你需要确保你的环境路径包含依赖项路径。这样就可以正常工作。
import os
from ctypes import *

path_to_deps = "C:\\myfolder"
os.environ['PATH'] = path_to_deps + os.pathsep + os.environ['PATH']
d = CDLL("C:\myfolder\proj2.dll")

更新:

Python 3.8更新说明中,添加了调用os.add_dll_directory(path)的功能,用于指定要搜索的dll目录。


3

列出[Python 3.Docs]: ctypes - Python的外部函数库

考虑到问题提出的时间,可以排除Python 3.8(但[SO]: PyWin32和Python 3.8.0 (@CristiFati的答案)仍然可能很有趣)。
基本上,这与[SO]: Can't import dll module in Python (@CristiFati的答案)相同。

对于.dll加载通用性,[SO]: Python Ctypes - 加载dll时抛出OSError: [WinError 193] %1不是有效的Win32应用程序(@CristiFati的答案)可能包含有用的信息。

现在的问题不是一个 MCVE 或者 REPREX[SO]: 如何创建一个最小可重现示例(reprex (mcve))),但我认为如果是这样,答案可能很明显,因此不需要提问 :)

调查

错误是 ERROR_PROC_NOT_FOUND 1270x7F)。

注意:仅当所有依赖项被成功加载(递归地)后,才会加载.dll 。如果其中一个依赖项失败,则该故障传播到顶部,并使用该错误使(原始).dll 载入失败。

2种情况下会自动发生什么:

  • 成功

    1. 找到并尝试加载proj2。它成功了。

    2. 找到并尝试加载proj1

      1. 由于它依赖于proj2,因此首先尝试加载proj2

      2. proj2已经在内存中(来自#1.

      3. proj2中搜索proj1所需的函数。它们都被找到了。


      因此,这一步骤(#2.)也成功了。

  • 失败

    1. 找到并尝试加载proj1

      1. 由于它依赖于proj2,因此首先尝试加载proj2

      2. 找到并加载proj2(如果没有找到,错误将是ERROR_MOD_NOT_FOUND

      3. proj2中搜索proj1所需的函数。显然,至少有一个函数没有被找到,程序崩溃了。

从上面可以得出的唯一结论是,在加载proj1.dll时,%PATH%中某处存在一个proj2.dll,并且操作系统正在自动加载它
它不是C:\myfolder\proj2.dll。它可能是一个旧版本,没有导出所需的函数,或者完全不相关。
我成功地使用两个简单(依赖).dll和一个.exe复现了崩溃(如果需要,我也会发布代码)。

img00

最简单的解决方案是在加载proj1.dll之前,将C:\myfolder(即proj2.dll所在的目录)添加到%PATH%中。请参阅[MS.Learn]: 动态链接库搜索顺序,并记住以下几个方面:

  • 将其附加(在末尾)不行,因为错误的.dlldir已经在%PATH%中(在正确的dir之前),并且会找到(和加载)错误的.dll

  • 您需要在错误的dir之前添加它(或者更好的方法是:从%PATH%中完全删除错误的dir)。要检查它在哪里,请使用(在cmd中)终端:

    where proj2.dll
    
  • 由于我不知道错误的.dlldir位置在%PATH%中的位置,因此在%PATH%的开头添加正确的.dlldir是可以的。个人认为,在Win系统目录之前添加目录并不是一个好习惯

  • 但是,如果proj2.dll位于“%SystemRoot%\System32”下,则必须删除(或重命名)它(该文件)

无论如何,%PATH% 的更改可以通过以下两种方式完成:
  • 在开始使用Python之前:

    set PATH=C:\myfolder;%PATH%
    
  • Python本身开始:

    os.environ["PATH"] = "C:\myfolder;" + os.environ["PATH"]
    

    对于Python 3.8,请查看答案开头的相关URL

也许您还想查看:


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接