从多个目录加载Win32模块

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

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 。 我可以改变所有的支持模块,那些是第三方库的redist。

我的代码调用LoadLibraryEx(name, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)加载每个插件。 对于core.dllplugin.dll ,这工作正常。

当我尝试加载a.dll ,它失败, 说没有finda_support.dll 。 没有关于core.dllsupport.dll错误,也许是因为它们已经在内存中。

我的怀疑是,当加载a_support.dllsupport.dll无法find,但这似乎是不寻常的,因为a.dll似乎在a_support.dll之前导入support.dll

这是模块的布局甚至可能使用? 系统能够使用已经加载的支持DLL,还是会去search它们并失败? 有没有办法通过体现来处理这个问题? 有没有办法使这个工作,或者我将不得不将所有模块重新放置到一个单一的目录?

编辑:在阿德里安·麦卡锡的build议,我运行加载序列与进程监视器跟踪,似乎当我调用LoadLibrary("root/a/bin/a.dll", ...) ,它开始search根目录,然后系统目录,然后通过path。 出于某种原因,它从不searcha/bin/ ,它非常应该。

我仔细检查path,并注意到我的调用加载plugin.dll在哪里使用错误的path(根,而不是根/核心/斌)。 无论哪种方式, core.dll加载正确。 修复后,我再次尝试,这次a.dll确实finda_support.dll ,似乎加载。 但是,这绝对没有意义,除非加载程序成功地使用support.dll从某处。 该procmon日志不显示它甚至试图加载support.dll再次,所以我不完全确定在这一点上,如果实际上有一个问题(除了加载器的行为没有任何意义)。

我建议使用Process Monitor来查看真正发生的事情。 你会看到,如果它在正确的地方看,是否a_support.dll打开,但无法加载,因为别的东西丢失,等等

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

如果没有,那么你的手上会有更多的工作。 我想你正在期待加载器将在DLL所在的目录中进行搜索。 可悲的是,事实并非如此。 相反,加载器将首先查看可执行文件的目录,然后查找DLL搜索顺序的其余部分。 这就是为什么a_support.dll无法加载,因为它不在可执行文件所在的目录。

模块已经在内存中的事实就在这一点上。 加载器去查找文件。 当它找到它想要的文件,然后检查是否已经加载。 如果是这样,那么它只是将引用计数碰到那个模块。 否则,将其加载到进程中。

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

你可以使用并行程序集,但是这听起来不太适合插件体系结构。

所以我认为剩下的主要选项是SetDllDirectory 。 在加载插件之前调用它。 你只需要添加a/bin到搜索路径,因为其余的模块都在可执行文件目录下,所以没有任何问题。 通过再次调用SetDllDirectory将该设置恢复为默认NULL ,一旦插件加载并解析了所有的导入,就会传递NULL

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

这是一个相当混乱的应用程序。

然后有一些问题:

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

我不能告诉你是否提供示例代码或实际代码。 你写了:

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

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

首先, LoadLibrary不会像你期望的那样使用相对路径。 来自MSDN :

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

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

如果你确实指的是LoadLibraryEx ,注意当你使用LOAD_WITH_ALTERED_SEARCH_PATH时,如果传递一个相对路径,你会得到“未定义的行为”。 再次引用MSDN:

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

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