一个进程是否可以加载同名但不同版本的两个dll?

4

我两天前在Microsoft社区发布了这个问题。我得到了一些好的想法和做了一些实验,但仍然失败了。为了更多帮助,我决定在这里发布这个问题。(原始文章: 一个进程是否可以加载两个具有不同版本的dll?

我开发了一个word全能应用程序,它是一个dll,可以被word和一些兼容word的应用程序加载(在这个问题中,我把它称为类似于word的应用程序,它们使用相同的ooxml标准)。我的插件使用CEF .net绑定cefglue(它又使用CEF(chromium嵌入框架))来呈现一些网页内容。下面是我插件项目中的一些细节。

这个插件的平台目标是Any CPU,由这个类似于word的应用程序加载。启动时,我通过调用Environment.Is64BitProcess检查这个插件是否在32位环境或64位环境下加载。之后,我根据加载我的插件dll的应用程序是64位还是32位来设置libcef.dll和其他依赖于应用程序的chromium库的适当库路径。例如,在64位word中,我的插件加载64位的libcef.dll。以下是一些伪代码:

// Files in cef library folder
// locales/
// swiftshader/
// cef.pak
// cef_100_percent.pak
// cef_200_percent.pak
// cef_extensions.pak
// chrome_elf.dll
// d3dcompiler_47.dll
// devtools_resources.pak
// icudtl.dat
// libcef.dll
// libEGL.dll
// libGLESv2.dll
// ...

private _cefPath;
    
void FindLibraryPath() {
  bool is64BitEnv = Environment.Is64BitProcess;
  SetLibraryAndResourcePath(is64BitEnv); // set _cefPath and other path here
}
    
void InitializeCef() {
  // Load cef library
  // this method use LoadLibraryEx with the flag
  // LOAD_WITH_ALTERED_SEARCH_PATH to load libcef.dll
  CefRuntime.Load(_cefPath);
  // other statements for cef initialization
  ...
}

private void ThisAddIn_Startup(object sender, System.EventArgs e) {
  FindLibraryPath();
  InitializeCef();
}

在Microsoft Word中这个很好,但不幸的是,在那个类似于Word的应用程序中会出现一些问题。原因如下:
那个类似于Word的应用程序也有自己的cef库来加载其网页内容(一些在线封面样式、页码样式等,这些都已准备好供当前文档使用)。如果我在这个类似于Word的应用程序中打开我的插件,然后点击一些按钮触发该应用程序加载其网页内容,那么这个类似于Word的应用程序就会崩溃。为了分析原因,我应该提供一些事实:

我的插件加载的库

myaddin.dll.net)-> cefglue.dll.net)-> libcef.dllnative dll,版本67)-> 其他Chromium本机dll

类似于Word的应用程序加载的库

|->myaddin.dll.net)-> cefglue.dll.net)-> libcef.dllnative dll,版本67)-> 其他Chromium本机dll
|->(加载自己的网页内容时)-> ...->libcef.dllnative dll,版本87)-> 其他Chromium本机dll(在其安装目录中)

->代表加载|代表一个独立的可能路径来加载dlls)

我使用绝对路径加载libcef.dll,如果我首先加载这个库,那么那个类似于Word的应用程序就不会加载其自己的libcef.dllLoadLibraryEx的内置规则?),它打算使用我的libcef. DLL然后崩溃,毕竟它需要版本87。如果类似于Word的应用程序首先加载了libcef.dll,那么我的插件将无法加载版本67的libcef.dll,然后我会得到版本不匹配异常。

总之,我需要隔离那个类似于Word的应用程序的相同名称但不同版本的libcef.dll,使其不感知由我的插件加载的同名DLL。有一些想法吗?谢谢。


我想问题与Word插件无关,所以问题与在基于.NET的应用程序中使用Chromium有关,我是对的吗? - Eugene Astafiev
是的,实际上这与 Word 插件无关。只是一个关于如何在进程中加载两个不同版本的 DLL 的问题。 - Lucas
@EugeneAstafiev 在一开始,我不知道为什么会出现这个错误,现在我可以理解了。经过很多次错误尝试后,我想到了一个解决方案(进行了实验,但还没有完成整个代码),使用LoadLibrary(而不是LoadLibraryEx)来加载my.dll,在my.dll中加载libcef.dll(如果我正确理解加载顺序,libcef.dll将加载其他dll,如chrome_elf.dll)。在my.dll中,我使用GetProcAddress来搜索cef函数,这些函数反过来又用于我的插件。my.dll是我的插件和libcef.dll之间的抽象层。 - Lucas
1个回答

3

这是不可能实现的,因为libcef.dll 有其他 DLL 依赖项,您无法控制它们的加载。详见Dynamic-Link Library Search Order。该页面的重要部分包括:

  • 如果同一模块名称的 DLL 已经加载到内存中,则系统仅在解析已加载的 DLL 前查找重定向和清单,而不管它位于哪个目录中。系统不会搜索 DLL。
  • 如果 DLL 具有依赖项,则系统将像只加载它们的模块名称一样搜索依赖的 DLL。即使第一个 DLL 是通过指定完整路径来加载的,也是如此。

唯一有效的解决方案是使您的插件严格依赖于另一个插件版本,并使用与 CEF 框架完全相同的版本构建您的插件。如果另一个插件正在使用自定义构建的 CEF 框架,则此方法可能无效。


你的话让我对期望感到泼了一盆冷水 :) - Lucas
我原以为可以使用 LoadLibrary 然后 GetProcAddress 来定位函数。尽管在加载库之前设置了不同的当前目录,但两次 LoadLibrary 返回的句柄是相等的。 :( - Lucas
这是链接页面上描述的确切行为,如果您没有重命名libcef.dll。如果您重命名它,进一步加载其他CEF库将返回相等的句柄。 - 273K
顺便提一下,我听说过“激活上下文”、“清单”或“sxs”的概念。我不知道它的确切含义,似乎在C++中我们可以使用非默认激活上下文来加载库,这样即使其他库或可执行文件使用与我的相同名称的库,它们也可以加载自己的版本。你知道这个想法吗?它也可以用在C#中吗?如果我可以在C++中使用这个想法,也许我可以通过另一个dll加载我的“libcef.dll”,该dll为我的插件提供cef接口。 - Lucas
你还在浪费时间。虽然你可以解决 libcef.dll 的问题,但其依赖项和第三方插件的问题是无法解决的。 - 273K
显示剩余5条评论

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