Visual Studio - 具有相同名称的非托管C++ DLL 在同一进程中共存

visual studio - Unmanaged C++ DLL's with same name coexisting in same process

本文关键字:DLL C++ 进程 Studio Visual      更新时间:2023-10-16

使用Visual Studio c++V10,我正试图找出如何构建DLL并解决DLL命名冲突。以下是详细信息。

S公司推出了一种名为M.EXE的产品。假设M.EXE安装在SBINM.EXE中。S公司静态链接到一个名为U.DLL的DLL,该DLL安装在SBINU.DLL中。U.DLL包含开源代码,并使用Visual C++编译器选项/Zc:wchar_t-构建,后者不将wchar识别为本机类型。

公司C提供一个名为O.DLL的DLL,并发布该DLL的API,并提供O.DLL的导入库。假设O.DLL安装在CBINO.DLL中。O.DLL静态链接到一个名为U.DLL的DLL,该DLL安装在CBINU.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将调用它。我想构建NODE.DLL,使其静态链接到C公司的O.DLL。但问题是,一旦M.EXE运行,它就从SBIN加载了U.DLL库,并且SBINU.DLL中的符号与CBINU.DLL中的符号略有不同,因为每个公司都是如何构建U.DLL的。因此,当M.EXE尝试加载NODE.DLL时,它失败了,因为当NODE.DLL加载需要U.DLLO.DLL时,CBINU.DLL所需的符号不存在,因为Windows认为U.DLL已经加载。

情况示意图如下:

M.EXE static link to -> SBINU.DLL
M.EXE dynamic link to -> NODE.DLL
NODE.DLL static link to  O.DLL
O.DLL static link to CBINU.DLL

实际上,我需要SBINU.DLLCBINU.DLL共存于同一进程空间中,并让M.EXE使用其版本的U.DLLO.DLL使用其版本U.DLL

请注意,我没有重建M.EXEO.DLL以更改它们各自加载U.DLL的方式的选项。它们来自第三方,因此静态链接无法更改。我也没有在O.DLL上使用LoadLibrary的选项,因为它是一个C++库,提供了一个导入库。

我相信清单是可以使用的,这样当我构建静态链接到O.DLL的NODE.DLL时,我会在NODE.DLL的清单中设置内容,以便O.DLL加载安装在CBINU.DLL中的U.DLL的自己的副本。我就是不知道该怎么做。理想情况下,我不想修改O.DLL的清单,但如果这是唯一的解决方案,我会接受它。

您可以在同一进程中通过用绝对路径加载一个或多个具有相同文件名的DLL。这确实需要动态加载DLL,但行为在其他方面是相同的。

您需要std::string moduleName = appPath + "sbinu.dll"; LoadModule(moduleName.c_str()),而不是在构建过程中进行链接。因为这对于需要加载哪个DLL是明确的,所以它允许您加载多个具有"相同"名称的DLL。

一旦加载了模块,就可以将每个必要的函数分配给函数指针,然后将它们包装起来,或者使用合法但很少使用的语法将函数指针作为普通函数调用(funcPtr(params)(。

如果您使用的是较新版本的Windows,则可以使用DLL清单来加强模块的版本控制/命名,并使EXE加载与通常不同的DLL。我不熟悉具体是如何做到这一点的,尽管MSDN上有相关文档(可能也在这里(。

您可以在运行时使用/delaylod链接器选项(VS项目属性中的linker/Input/Delay Loaded DLL(和自定义挂钩以编程方式解析源DLL。在一个源文件中,您需要定义并注册一个delaylod-hook函数。在钩子函数的dliNotePreLoadLibrary通知处理程序中,只需使用指向所需DLL的显式路径调用LoadLibrary,然后将DLL的HMODULE传递回delaylod代码。delaylod代码将把导入的函数解析到您给它的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;

尝试使用LoadLibrary和GetProcAddress。这将需要重新构造代码,以便在任何地方都使用函数指针。参见:

LoadLibrary 的MSDN网页

你很幸运U.DLL是开源的。您需要构建一个同时支持/Zc:wchar_t-/Zc:wchar_t函数的版本。第一个选项仅将wchar_t-定义为unsigned short。对于每个没有wchar_t参数的函数,您会收到大量关于重复符号的链接器警告,但否则您只会得到一个更胖的DLL。

请注意,如果有任何全局或static变量使用wchar_t,那么您也会有它们的两个副本。但如果你在一个过程中硬塞进两份U.DLL,也会产生同样的效果。