从多个目录加载 Win32 模块

发布于 2024-12-22 01:38:01 字数 1521 浏览 1 评论 0原文

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

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

在本例中,a.dll 导入 core.dllsupport.dll 、和a_support.dll(它们在导入表中按该顺序排列)。 a_support.dll 导入 support.dll。我可以更改除支持模块之外的所有模块,这些模块是第三方库的 redists。

我的代码调用 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.dlla_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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

乞讨 2024-12-29 01:38:01

我建议使用进程监视器来查看到底发生了什么。您将看到它是否在正确的位置,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.

荆棘i 2024-12-29 01:38:01

当然,一种解决方案是将所有 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 add a/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 calling SetDllDirectory again, passing NULL, once the plugin has loaded and resolved all of its imports.

If you have multiple sub-directories then use AddDllDirectory.

烟凡古楼 2024-12-29 01:38:01

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

那么有一些问题:

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

This is a rather confusing application.

Some questions then:

  • Which dll's does app.exe implicitly import?
  • core.dll is both implicitly loaded by a.dll AND as a plugin via LoadLibraryEx?
  • How was the call to LoadLibraryEx on /plugin.dll ever succeeding? If the path was FQ and did not point at an actual dll, LoadLibrary should have failed outright on that dll.
厌味 2024-12-29 01:38:01

我无法判断您提供的是示例代码还是真实代码。您写道:

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

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

首先,LoadLibrary 不会执行您期望的相对路径操作。来自 MSDN

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

基本上,您给它一个完整路径并获取该文件,或者让它在所有“常用”位置搜索名称。如果你给它一个相对路径,它基本上会忽略该路径,获取名称,并在通常的位置查找。

如果您确实指的是 LoadLibraryEx,请注意,当您使用 LOAD_WITH_ALTERED_SEARCH_PATH 时,如果传递的是相对路径,则会出现“未定义的行为”。再次引用 MSDN:

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

其次,您使用正斜杠而不是反斜杠。 LoadLibraryLoadLibraryEx 都不喜欢这些。

I can't tell if you're giving sample code or real code. You wrote:

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

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:

To load a module from a relative path without searching any other
path, use GetFullPathName to get a nonrelative path and call
LoadLibrary with the nonrelative path. For more information on the DLL
search order, see Dynamic-Link Library Search Order.

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 use LOAD_WITH_ALTERED_SEARCH_PATH you get "undefined behavior" if handed a relative path. Again quoting MSDN:

If this value is used and lpFileName specifies a relative path, the behavior is undefined.

Second, you have forward slashes instead of backslashes. Neither LoadLibrary nor LoadLibraryEx likes those.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文