从多个目录加载Win32模块

时间:2011-12-20 19:45:38

标签: windows winapi dll loadlibrary

我有一个程序可以将插件存储在多个目录中,如下所示:

root/
  core/bin/
    app.exe
    core.dll
    plugin.dll
    support.dll
  a/bin/
    a.dll
    a_support.dll

在此示例中,a.dll导入core.dllsupport.dlla_support.dll(它们在导入表中按此顺序排列)。 a_support.dll导入support.dll。我可以更改除支持模块以外的所有模块,这些模块是第三方库的重写者。

我的代码调用LoadLibraryEx(name, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)来加载每个插件。对于core.dllplugin.dll,这很好用。

当我尝试加载a.dll时,未能找到a_support.dll 。没有关于core.dllsupport.dll的错误,可能是因为它们已经在内存中。

我怀疑在加载a_support.dll时无法找到support.dll,但这似乎不正常,因为a.dll似乎在support.dll之前导入a_support.dll

是否可以使用模块的布局?系统是否能够使用已经加载的支持DLL,还是会搜索它们并失败?是否有办法通过清单来处理这个问题?有没有办法使这项工作,或者我必须将所有模块重新定位到一个目录?

编辑:在Adrian McCarthy的建议下,我使用Process Monitor跟踪运行加载序列,似乎当我调用LoadLibrary("root/a/bin/a.dll", ...)时,它首先搜索根目录,然后是系统目录,然后通过路径。出于某种原因,它从不搜索a/bin/,这非常应该。

我仔细检查了路径,并注意到我调用加载plugin.dll使用错误路径(root,而不是root / core / bin)。无论哪种方式,core.dll都正确加载。修好之后,我再次尝试了,这次a.dll找到a_support.dll并且似​​乎加载了。但是,这绝对没有意义,除非加载器成功地使用support.dll来自某个地方。即使尝试再次加载support.dll,procmon日志也不会显示它,所以我不确定此时是否确实存在问题(除了加载器的行为没有意义)。

4 个答案:

答案 0 :(得分:1)

当然,一种解决方案是将所有DLL放在同一目录中,与.exe相同的目录。如果你能做到这一点,那肯定是最简单的方法。

如果没有,那么你的手上会有更多的工作。我猜你期望加载器将搜索DLL所在的目录。可悲的是,它没有。相反,加载器将首先查看可执行文件的目录,然后查看DLL search order的其余部分。这就是a_support.dll无法加载的原因,因为它与可执行文件不在同一目录中。

模块已经在内存中的事实不是重点。加载器去寻找文件。当它找到它想要的文件时,然后检查它是否已经加载。如果是这样,那么它只是将引用计数颠倒到该模块。否则它会将其加载到流程中。

您可以切换到使用LoadLibrary进行所有DLL加载,并始终明确路径。这可能不方便。

您可以使用并排的程序集,但这听起来与插件架构不兼容。

所以我认为剩下的主要选项是SetDllDirectory。在加载插件之前调用它。您只需要将a/bin添加到搜索路径,因为其余模块位于可执行目录中,因此可以毫无问题地找到它们。通过再次调用SetDllDirectory将此设置恢复为默认值,并在插件加载后传递NULL并解决所有导入。

如果您有多个子目录,请使用AddDllDirectory

答案 1 :(得分:1)

我建议使用Process Monitor来查看实际发生的情况。您将看到它是否正在查找正确的位置,a_support.dll是否已打开但无法加载,因为缺少其他内容等。

答案 2 :(得分:1)

这是一个相当令人困惑的应用程序。

然后有些问题:

  • app.exe隐式导入哪个dll?
  • core.dll是由a.dll和通过LoadLibraryEx作为插件隐式加载的吗?
  • 对/plugin.dll上的LoadLibraryEx的调用是如何成功的?如果路径是FQ并且没有指向实际的dll,则LoadLibrary应该在该dll上完全失败。

答案 3 :(得分:0)

我不知道你是在提供示例代码还是实际代码。你写道:

LoadLibrary("root/a/bin/a.dll", ...)

如果那是真正的代码,那么这里有两个问题。

首先,LoadLibrary没有达到您对相对路径的期望。来自MSDN

  

从相对路径加载模块而不搜索任何其他模块   path,使用GetFullPathName获取非相关路径并调用   LoadLibrary具有非相对路径。有关DLL的更多信息   搜索顺序,请参阅动态链接库搜索顺序。

基本上,你给它一个完整的路径并获取该文件,或者你让它在所有“通常”的位置搜索一个名字。如果你给它一个相对路径,它基本上忽略了那条路径,抓住了名字,并查看了通常的位置。

如果您真的想要LoadLibraryEx,请注意当您使用LOAD_WITH_ALTERED_SEARCH_PATH时,如果提交了相对路径,则会获得“未定义的行为”。再次引用MSDN:

  

如果使用此值并且lpFileName指定相对路径,则行为未定义。

其次,你有正斜杠而不是反斜杠。 LoadLibraryLoadLibraryEx都不喜欢这些。