从多个目录加载 Win32 模块
我有一个程序将插件存储在多个目录中,如下所示:
root/
core/bin/
app.exe
core.dll
plugin.dll
support.dll
a/bin/
a.dll
a_support.dll
在本例中,a.dll
导入 core.dll
、support.dll
、和a_support.dll
(它们在导入表中按该顺序排列)。 a_support.dll
导入 support.dll
。我可以更改除支持模块之外的所有模块,这些模块是第三方库的 redists。
我的代码调用 LoadLibraryEx(name, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
来加载每个插件。对于core.dll
和plugin.dll
,这工作得很好。
当我尝试加载 a.dll
时,失败并提示找不到 a_support.dll
。 core.dll
或 support.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 日志也不会显示它,所以我现在不完全确定是否确实存在问题(除了加载程序的行为没有意义) )。
I have a program which stores plugins in multiple directories, like so:
root/
core/bin/
app.exe
core.dll
plugin.dll
support.dll
a/bin/
a.dll
a_support.dll
In this example, a.dll
imports core.dll
, support.dll
, and a_support.dll
(they are in that order in the import table). a_support.dll
imports support.dll
. I can change all but the support modules, those are redists of a third-party library.
My code calls LoadLibraryEx(name, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
to load each plugin. For core.dll
and plugin.dll
, this works fine.
When I try to load a.dll
, it fails saying a_support.dll
was not found. No errors about core.dll
or support.dll
, perhaps because they're already in memory.
My suspicion is that when a_support.dll
is loaded, support.dll
cannot be found, but this seems unusual as a.dll
appears to import support.dll
before a_support.dll
.
Is this layout of modules even possible to use? Will the system be able to use the already-loaded support DLLs, or will it go searching for them and fail? Would there be a way to handle this through manifests? Is there a way to make this work, or will I have to relocate all modules into a single directory?
Edit: At Adrian McCarthy's suggestion, I ran the loading sequence with Process Monitor tracking, and it seems that when I call LoadLibrary("root/a/bin/a.dll", ...)
, it starts by searching the root directory, then system directories, then down through the path. For some reason, it never searches a/bin/
, which it very much should.
I double-checked the paths, and noticed that my calls to load plugin.dll
where using the wrong path (root, instead of root/core/bin). Either way, core.dll
was loading correctly. After fixing that, I tried it again and this time a.dll
does find a_support.dll
and seems to load. However, that makes absolutely no sense, unless the loader is successfully using support.dll
from... somewhere. The procmon log doesn't show it even attempting to load support.dll
again, so I'm not entirely sure at this point if there actually is a problem (besides behavior from the loader that makes no sense).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我建议使用进程监视器来查看到底发生了什么。您将看到它是否在正确的位置,a_support.dll 是否已打开但由于缺少其他内容而无法加载,等等。
I suggest using Process Monitor to see what's really happening. You'll see if it's looking in the right place, whether a_support.dll is opened but not loadable because something else is missing, etc.
当然,一种解决方案是将所有 DLL 放置在与 .exe 相同的目录中。如果你能做到这一点,这肯定是最简单的方法。
如果没有,那么您手上的工作就会多一些。我猜您希望加载程序在 DLL 所在的目录中进行搜索。遗憾的是事实并非如此。相反,加载程序将首先查找可执行文件的目录,然后查找 DLL 搜索顺序。这就是
a_support.dll
无法加载的原因,因为它与可执行文件不在同一目录中。模块已经在内存中这一事实不是重点。加载器开始寻找文件。当它找到所需的文件时,它会检查该文件是否已加载。如果是这样,那么它只是将引用计数增加到该模块。否则它将加载到进程中。
您可以切换到使用
LoadLibrary
来加载所有 DLL,并且始终明确路径。这大概是不方便吧。您可以使用并排程序集,但这听起来与插件架构不太兼容。
所以我认为剩下的主要选项是
SetDllDirectory
。在加载插件之前调用此函数。您只需将a/bin
添加到搜索路径,因为其余模块都在可执行目录中,因此可以轻松找到。插件加载并解析其所有导入后,通过再次调用SetDllDirectory
并传递NULL
来将此设置恢复为默认值。如果您有多个子目录,请使用
AddDllDirectory
。Certainly one solution would be to place all DLLs in the same directory, the same directory as the .exe. If you can bring yourself to do that it would be the simplest approach for sure.
If not then you will have a bit more work on your hands. I guess you are expecting that the loader will search in the directory where the DLL lives. Sadly it doesn't. Instead the loader will look first in the executable file's directory, and then the rest of the DLL search order. This is why
a_support.dll
fails to load, because it is not in the same directory as the executable.The fact that modules are already in memory is beside the point. The loader goes looking for the file. When it finds the file that it wants it then checks to see if it is already loaded. If so then it simply bumps the reference count to that module. Otherwise it loads it into the process.
You could switch to using
LoadLibrary
for all DLL loads and always being explicit about the path. That's probably inconvenient.You could use side-by-side assemblies but that doesn't sound very compatible with a plugin architecture.
So I think the main remaining option is
SetDllDirectory
. Call this just before you load the plugin. You only need to adda/bin
to the search path since the rest of the modules are in the executable directory and so will be found without trouble. Restore this setting to its default by callingSetDllDirectory
again, passingNULL
, once the plugin has loaded and resolved all of its imports.If you have multiple sub-directories then use
AddDllDirectory
.这是一个相当令人困惑的应用程序。
那么有一些问题:
This is a rather confusing application.
Some questions then:
我无法判断您提供的是示例代码还是真实代码。您写道:
如果那是真正的代码,那么这里有两个问题。
首先,
LoadLibrary
不会执行您期望的相对路径操作。来自 MSDN:基本上,您给它一个完整路径并获取该文件,或者让它在所有“常用”位置搜索名称。如果你给它一个相对路径,它基本上会忽略该路径,获取名称,并在通常的位置查找。
如果您确实指的是
LoadLibraryEx
,请注意,当您使用LOAD_WITH_ALTERED_SEARCH_PATH
时,如果传递的是相对路径,则会出现“未定义的行为”。再次引用 MSDN:其次,您使用正斜杠而不是反斜杠。
LoadLibrary
和LoadLibraryEx
都不喜欢这些。I can't tell if you're giving sample code or real code. You wrote:
If that's the real code, there are two problems here.
First,
LoadLibrary
doesn't do what you'd expect with a relative path. From MSDN:Basically, you give it a full path and get that file, or you let it search for a name in all the "usual" locations. If you give it a relative path, it basically ignores that path, grabs the name, and looks in the usual locations.
If you really meant
LoadLibraryEx
, note that when you useLOAD_WITH_ALTERED_SEARCH_PATH
you get "undefined behavior" if handed a relative path. Again quoting MSDN:Second, you have forward slashes instead of backslashes. Neither
LoadLibrary
norLoadLibraryEx
likes those.