同名的未经管理的C++ DLL在同一进程中共存

7
使用Visual Studio c++ V10,我正在尝试构建DLL并解决DLL命名冲突。以下是详细信息。
公司S提供名为M.EXE的产品。假设M.EXE安装在\S\BIN\M.EXE中。公司S静态链接到名为U.DLL的DLL,该DLL安装在\S\BIN\U.DLL中。U.DLL包含开源代码,并使用了Visual C ++编译器选项/Zc:wchar_t-构建,该选项不认识wchar作为本机类型。
公司C提供名为O.DLL的DLL,并发布此DLL的API,并提供O.DLL的导入库。假设O.DLL安装在\C\BIN\O.DLL中。O.DLL静态链接到名为U.DLL的DLL,该DLL安装在\C\BIN\U.DLL中。U.DLL构建于相同的开源代码上,但是使用了Visual C ++编译器选项/Zc:wchar_t,该选项将wchar_t识别为本机类型。
理想情况下,公司C和公司S将同意使用相同的Visual C ++选项构建U.DLL,但这是不可能的。
来自公司S的M.EXE可以扩展,我可以使用未管理的C++构建自己的DLL,称之为NODE.DLL,如果我正确设置了一切,M.EXE将调用该DLL。我希望构建NODE.DLL,使其静态链接到公司C的O.DLL。但问题在于,一旦M.EXE运行,它就已经加载了来自\S\BINU.DLL库,并且由于每个公司如何构建U.DLL的原因,\S\BIN\U.DLL中的符号略有不同于\C\BIN\U.DLL中的符号。因此,当M.EXE尝试加载NODE.DLL时,会失败,因为当NODE.DLL加载需要U.DLLO.DLL时,从\C\BIN\U.DLL所需的符号不存在,因为Windows认为U.DLL已经加载。
情况的示意图如下:
M.EXE static link to -> \S\BIN\U.DLL
M.EXE dynamic link to -> NODE.DLL
NODE.DLL static link to  O.DLL
O.DLL static link to \C\BIN\U.DLL

有效地,我需要\S\BIN\U.DLL\C\BIN\U.DLL同时存在于同一进程空间中,并且让M.EXE使用其自己版本的U.DLL,而O.DLL则使用其自己版本的U.DLL
请注意,我无法重新构建M.EXEO.DLL以更改它们加载U.DLL的方式。它们来自第三方,因此静态链接不能被更改。我也无法使用LoadLibrary加载O.DLL,因为它是一个带有导入库的C++库。
我相信可以使用清单文件,这样当我构建静态链接到O.DLL的NODE.DLL时,我可以在NODE.DLL的清单文件中设置O.DLL,使其加载安装在\C\BIN\U.DLL的它自己的U.DLL。我只是无法弄清楚如何做到这一点。理想情况下,我不想修改O.DLL的清单文件,但如果这是唯一的解决办法,我将接受这种方案。

DLL无处不在!(抱歉,我忍不住了)您好,欢迎。 - Jens Bergvall
问题的实质在于 O.DLL 在其 IAT 中指定了未经限定的 U.DLL 作为链接对象。 - MSalters
4个回答

4
在同一进程中,您可以通过使用绝对路径加载其中一个或多个DLL来拥有具有相同文件名的多个DLL。这确实需要动态加载DLL,但行为与其他情况相同。
在构建过程中不要链接,而是需要std :: string moduleName = appPath +“\ s \ bin \ u.dll”; LoadModule(moduleName.c_str())。因为这是明确指出需要加载哪个DLL,它允许您加载多个具有“相同”名称的DLL。
一旦加载了模块,您就可以将每个必要的函数分配给函数指针,然后包装这些函数或使用合法但很少使用的函数指针调用函数的语法funcPtr(params))。
如果您正在使用较新版本的Windows,则可以使用DLL清单来加强模块的版本控制和命名,并导致EXE加载与通常不同的DLL。我不熟悉如何确切地完成此操作,尽管MSDN(以及可能在此处)有记录。

这不是一个选项,因为我没有重新构建 M.EXE 或 O.DLL 的能力。它们来自不同的第三方。 - Irv
如果其中一个模块使用了部分路径、绝对路径或者使用了动态加载,那么你可能可以通过。否则,清单文件将是你唯一的机会。 - ssube
我相信清单文件是唯一的选择。我想知道在清单文件中应该放什么来使事情正常工作。M.EXE和O.DLL都使用导入库构建,然后使用普通的DLL加载规则加载U.DLL。每个公司都将自己的版本的U.DLL放在自己的BIN目录中。当运行M.EXE时,它会获取其自己的U.DLL副本。当您编写使用O.DLL的标准EXE时,它会获取其自己的U.DLL副本。 - Irv

1
您可以使用/delayload链接器选项(VS项目属性中的链接器/输入/延迟加载的DLL)以及自定义钩子,在运行时通过编程方式解决源DLL。在您的一个源文件中,您需要定义并注册一个延迟加载钩子函数。在钩子函数的dliNotePreLoadLibrary通知处理程序中,只需使用显式路径调用LoadLibrary到所需的DLL,然后将DLL的HMODULE传回给延迟加载代码。无论同名的另一个DLL是否已加载到进程中,延迟加载代码都将将导入的函数解析为您提供的DLL。
从我的角度来看,您的钩子将类似于以下内容(其中MyCustomLoadLibrary需要替换为在冲突情况下调用LoadLibrary并使用完整路径到所需DLL的代码,否则仅使用未限定的文件名):
#include <delayimp.h>
FARPROC WINAPI MyDliNotifyHook( unsigned dliNotify, PDelayLoadInfo pdli )
{
  if( dliNotify == dliNotePreLoadLibrary )
    return (FARPROC)MyCustomLoadLibrary( pdli->szDll );
  return NULL;
}
extern "C" PfnDliHook __pfnDliNotifyHook2 = MyDliNotifyHook;

只有当NODE.DLL链接到冲突的DLL时才有效。重新阅读您的问题后,我发现您说其中一个依赖的DLL链接到冲突的DLL。在这种情况下,您将不得不通过编程解决依赖DLL的导入地址表。这并不难做,但在此描述步骤过于复杂。 - Owen Wengerd
“可以给我一些指示,在哪里可以阅读更多信息,以编程方式解决依赖 DLL 的导入地址表?” - harper
1
有两种情况:一种是加载器可以成功解析所有导入(尽管是错误的DLL)的情况;另一种情况是加载器无法解析导入。如果您的情况是前者,我可以提供一些指针。如果您的情况是后者,则必须基本上复制Windows加载器代码(解析导入并应用修复,然后调用入口点函数)。几乎没有文档,我不知道任何公共信息来源。这不是胆小的人,但是有可能:我已经成功地做到了。 - Owen Wengerd
如果你的情况是前者,我可以提供一些指针。是的,请先生。 - harper
Harper,我刚意识到你不是原帖作者。由于这与原帖话题无关,请自行提出新问题。 - Owen Wengerd
显示剩余2条评论

0

你很幸运,因为U.DLL是开源的。你需要构建一个支持两个函数/Zc:wchar_t-/Zc:wchar_t的版本。第一个选项仅将wchar_t-定义为unsigned short。对于每个没有wchar_t参数的函数,你会得到大量的链接器警告,但除此之外,你最终只会得到一个更大的DLL。

请注意,如果有任何使用wchar_t的全局或static变量,那么你也将有两个副本。但如果你在一个进程中强行塞入两个U.DLL的副本,你也会得到同样的效果。


0

尝试使用LoadLibrary和GetProcAddress。不过,这将需要重构您的代码以在所有地方使用函数指针。请参见:

MSDN网页上的LoadLibrary


这不是一个选项,因为我没有重建 M.EXE 或 O.DLL 的能力。它们来自不同的第三方。 - Irv

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