【问题标题】:How can you make a C++/CLI DLL resolve a dependency on managed assemblies (DLLs), without using the GAC?如何在不使用 GAC 的情况下使 C++/CLI DLL 解决对托管程序集 (DLL) 的依赖?
【发布时间】:2020-01-25 02:24:48
【问题描述】:

我们目前将 ODBC 驱动程序构建为 4 个 DLL:

  1. 实现 ODBC (C) API 的 DLL,主要用 C++ 实现, 使用 C++/CLI 编写的一些“胶水”代码,用于与 #2、#3 和 #4 交互

  2. 包含托管程序集(用 C# 编写)的 DLL,该程序集定义了用于 #1 和 #4 相互通信的“基本”接口。

  3. 另一个 DLL 包含一个托管程序集,该程序集依赖于 #2,并为 #2 中的类定义了一些扩展
  4. 另一个包含托管程序集的 DLL,其中包含“业务 驱动程序的逻辑',取决于#2 & #3

为了部署驱动程序,我们将 DSN 配置为指向 #1,并将 #2、#3 和 #4 放入 GAC。

我们有一位客户想要完全避开 GAC。我知道将 #2、#3 和 #4 与加载 #1 'works'的应用程序放在同一目录中,但这不是一个好的解决方案,因为许多不同的应用程序可能会使用该驱动程序。

我们如何设置它以便在没有 GAC 的情况下解决依赖关系?我尝试创建清单文件(基于https://docs.microsoft.com/en-us/windows/win32/sbscs/assembly-manifests),但这似乎不起作用(EEFileLoadException 异常被抛出,因为它找不到托管程序集,当我从GAC)。我将清单文件和所有 .DLL 文件放在同一个目录中。

通过谷歌搜索,我找不到任何好的文档/示例。

【问题讨论】:

  • 您必须放弃 C++/CLI 并自己托管 CLR,这样您就可以在使用主 appdomain 之前对其进行初始化。大改写。报价合适的价格和它自己解决的问题。
  • 真的没有“类似清单文件”的方法吗?您必须在运行时实际调用 CLR API?
  • 我们目前在入口点使用docs.microsoft.com/en-us/cpp/dotnet/…,硬编码的应用域为 1(意味着是“默认”应用域)。也许我们可以创建自己的 appdomain 并使用它来代替......一位同事正在测试尝试配置 CLR 如何加载程序集,并注意到它延迟加载托管程序集,直到第一次运行托管代码,但仅在32 位,而不是 64 位(不知道为什么)
  • 如果每个应用程序都附带 #2 #3 和 #4,真正的问题是什么? “许多不同的应用程序可能会使用驱动程序”是什么意思?
  • 当您在机器上安装 ODBC 驱动程序时,任何应用程序都可以使用它。由于 ODBC 是标准 API,因此无法列出所有可能需要考虑的应用程序。

标签: c++ .net manifest gac mixed-mode


【解决方案1】:

我不确定我是否完全理解你的问题,但我会尽力回答:

在尝试解决这个问题时,我们应该记住以下几点:

  1. Fusion 不会启动(因此依赖解析),直到您位于未解析类型的第一次出现所在的堆栈帧中。
  2. 我们可以假设 CLR 已经加载到宿主进程中,并且我们至少有一个 Appdomain。 (否则,您必须考虑这两种情况 - 特别是被默认情况下不加载 CLR 的进程消耗。在这种情况下您想做什么?自己加载 CLR?隐式?显式?如果是这样,您如何配置您的 Appdomain?您的 App Root 在哪里?您的探测路径是什么?)

鉴于这两点,您可以订阅AppDomain.AssemblyResolve Event。但是您应该以这种方式重构您的代码,因此 fusion 在您设法订阅之前不会尝试解析您的程序集。在处理程序中,仅处理正在解析 YOUR 程序集的事件,并从您想要的任何位置加载它们 - 例如,从本地库所在的同一路径。

补充阅读:

【讨论】:

【解决方案2】:

根据 cmets 更新...

我在此处的 Microsoft 文档中发现了一个有趣的点:docs.microsoft.com/en-us/windows/win32/dlls/... --“如果 DLL 具有依赖关系,系统会搜索依赖的 DLL,就好像它们是只加载了它们的模块名称。即使第一个 DLL 是通过指定完整路径加载的,也是如此。这意味着如果这些 dll 由任何其他进程加载,则将使用内存中具有该名称的版本。

基本上微软的文档所说的是:

除非计算机上存在与#2-#4 模块名称相同的其他dll,否则您无需担心。无论您是否将这些 dll 放入 GAC。 #1 将由应用程序使用您指定的任何路径加载,#2-#4 将仅按模块名称加载。

您唯一的问题是,如果您的模块 #2-#4 没有良好的独特名称,并且可能存在其他具有相同名称和不同定义的模块。这是因为它们将仅按名称加载。

至于执行顺序...#1 是您的入口点,如果它依赖于 #2,它将在任何执行之前加载,依此类推。除非你对你的构建指令做了一些明确的事情,但你现在的实现也会遇到这种情况。

【讨论】:

  • 还没有读完你的答案,只是想澄清#1 不是应用程序,“应用程序”是任何使用 ODBC API 的旧程序和恰好加载了我们的驱动程序。
  • “其次,在几乎任何开发环境中都可以轻松设置已部署应用程序的引用库的本地副本。” - 如果驱动程序是.exe,我认为这会很好。由于它是由其他应用程序加载的 DLL,所以我认为它没有自己的“激活上下文”? (我是在重复一个同事用的一个术语,细节我不完全理解)
  • 通常这不会有问题。但是,我在此处的 Microsoft 文档中发现了一个有趣的点:docs.microsoft.com/en-us/windows/win32/dlls/… --“如果 DLL 具有依赖关系,系统会搜索依赖的 DLL,就好像它们仅加载了它们的模块名称一样。即使第一个 DLL 是通过指定完整路径加载。”这意味着如果这些 dll 由任何其他进程加载,则将使用内存中具有该名称的版本。
  • 鉴于您的 dll #1-#4 几乎肯定会引用其他 Microsoft 程序集,因此您无法合理地知道在加载驱动程序时将加载哪些版本(GAC 或显式本地副本) ,除非您确定它们在机器上的其他任何地方都不存在。
  • 客户只担心将他们的依赖项放在GAC中,他们并不关心系统库
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-13
  • 2011-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多