COM 互操作加载程序集时 AppDomain 的路径

Posted

技术标签:

【中文标题】COM 互操作加载程序集时 AppDomain 的路径【英文标题】:Path of AppDomain when assembly is loaded by COM interop 【发布时间】:2012-04-23 14:57:36 【问题描述】:

为 COM 互操作注册的 .NET 程序集与一些应在程序集中加载类型的反射代码相结合会导致一些奇怪的行为。我已经分析了调试器中发生了什么,并且我已经搜索了网络以找到解决方案。 我找到了很多有帮助的文章,但没有什么能让我完全解决问题。

问题概述

A 有一个不是 .NET 的 exe 文件(在我的情况下是 VB6 应用程序)。它位于文件夹 A 中。 我在文件夹 B 中有一些 .NET dll。其中一个是 COM dll。 exe 文件实例化 COM .NET 程序集的 .NET 对象的 COM 实例。 那么 AppDomain 的主路径是文件夹 A,但我希望它是文件夹 B。由于它是文件夹 A,我的 .NET 代码中的一些反射类型加载失败。

详情如下:

我有一个 VB6 应用程序。 exe文件位于文件夹A中。 在里面我有一个 VB6 语句

Set DotNetE2 = CreateObject("MyDotNet.E2")

这将创建一个注册用于 COM 互操作的 .NET 类的实例。 .NET 类的标头如下所示:

namespace MyDotNet.E2.COM

   [ComVisible(true)]
   [Guid("776FF4EA-2F40-4E61-8EF3-08250CB3712B")]
   [ProgId("MyDotNet.E2")]
   [ClassInterface(ClassInterfaceType.AutoDual)]
   public class E2
   

我的 .NET 程序集“MyDotNet.E2.COM.dll”位于文件夹 B 中。 此程序集引用了位于同一文件夹中的其他两个名为 E3 和 E4 的 .NET 程序集。 E3 没有对 E4 的引用。 我能够按预期在这些程序集中执行代码,因此引用是可以的。 到现在为止还挺好。 现在,我在 E3 中有一些代码试图对 E4 中的类型进行一些反思。 这失败了。

代码如下:

string dotnetPath = Path.GetDirectoryName(
                 Assembly.GetExecutingAssembly().Location);
string mainDir = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
string otherDirs = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath;

Assembly assembly = Assembly.LoadFrom(Path.Combine(dotnetPath, "E4.dll"));
Type mytype = assembly.GetType("MyDotnet.E4.MyForm");

观察

dotnetPath 与 mainDir 不同。 我的类型为空。预期结果是类型实例。 如果我将 exe 文件连同 .NET 程序集一起移动到文件夹 B,它就可以工作。那么dotnetPath和mainDir是一样的。 如果我在 E2 而不是 E4 中执行反射代码,它可以工作,即使 dotnetPath != mainDir. 但在我所概述的场景中,它不起作用。

通过在配置文件中指定这些文件夹,我发现了一些关于将其他文件夹添加到 PrivateBinPath 中的 AppDomain 的提示。但我在这方面没有成功。我试图向我的 COM .NET 文件和我的 VB6 exe 文件添加一个配置文件,但我的 PrivateBinPath 属性没有发生任何事情。这是我尝试添加的配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
     <probing privatePath="..\..\FolderB"/>
    </assemblyBinding>
  </runtime>
</configuration>

不要让我重组我的程序集和类型。项目比较复杂,这是最好的架构。

【问题讨论】:

【参考方案1】:

我自己设法解决了这个问题。 关键是在系统无法解析程序集时触发的 AssemblyResolve 事件。 这里解释: http://msdn.microsoft.com/library/system.appdomain.assemblyresolve

这似乎是 .NET 框架中的一个错误,我必须使用此事件,但结果证明这个解决方法相当不错。

string dotnetPath = Path.GetDirectoryName(
                                   Assembly.GetExecutingAssembly().Location);
string mainDir = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;

if (!mainDir.Equals(dotnetPath, StringComparison.CurrentCultureIgnoreCase))

   // This will happen if .NET process is fired 
   // from a COM call from another folder.
   // Solution: an event is fired if assembly-resolving fails.
   AppDomain.CurrentDomain.AssemblyResolve += 
                         new ResolveEventHandler(CurrentDomain_AssemblyResolve);

事件处理程序相当简单:

Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)

   foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
   
      if (assembly.FullName == args.Name) return assembly;
   
   return null;

【讨论】:

H,我有同样的问题,但到目前为止我还没有解决它。我的 C++ exe 调用了一个 .NET COM Interop dll,我们调用的是 C.dll,它位于同一个文件夹中,但所有 C.dll 依赖项都在一个子文件夹中。到目前为止,除非我将所有依赖项放在同一个文件夹中,否则我无法调用 C.dll。有什么想法吗?

以上是关于COM 互操作加载程序集时 AppDomain 的路径的主要内容,如果未能解决你的问题,请参考以下文章

将多个程序集版本加载到多个 AppDomain 中

在 AppDomain 问题中加载具有依赖项的程序集

从 byte[] 动态加载程序集

有没有办法在使用c#扫描所有程序集时识别我的自定义程序集?

在不知道类型的情况下将程序集从文件加载到自定义 AppDomain

WPF 资源加载在 LoadFrom 加载上下文中失败