COM 互操作加载程序集时 AppDomain 的路径 [英] Path of AppDomain when assembly is loaded by COM interop

查看:24
本文介绍了COM 互操作加载程序集时 AppDomain 的路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为 COM Interop 注册的 .NET 程序集与一些应在程序集中加载类型的反射代码相结合会导致一些奇怪的行为.我已经分析了调试器中发生的事情,并在网上搜索以找到解决方案.找了很多有帮助的文章,但都没有让我彻底解决问题.

A .NET assembly that is registered for COM Interop combined with some reflection code that shall load types in assemblies results in some strange behavior. I have analyzed what is going on in the debugger and I have searched the net to find a solution. I have found many articles with help, but nothing that have made me solve the problem completely.

问题概述

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

A have an exe-file that is not .NET (in my case a VB6 application). It resides in folder A.
I have some .NET dlls in folder B. One of them is a COM dll.
The exe-files instantiates a COM instance of a .NET object of the COM .NET assembly. Then the main path of the AppDomain is folder A, but I would like it to be folder B. Since it is folder A, some reflection type-loading inside my .NET code fails.

详情如下:

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

I have a VB6 application. The exe file resides in folder A. Inside it I have a VB6 statement

Set DotNetE2 = CreateObject("MyDotNet.E2")

这将创建我的 .NET 类的一个实例,该实例为 COM 互操作注册..NET 类的标题如下所示:

This creates an instance of my .NET class that is registred for COM interop. The header of the .NET class looks like this:

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 中的类型进行一些反思.这失败了.

My .NET assembly 'MyDotNet.E2.COM.dll" resides in folder B. This assembly has references to two other .NET assemblies called E3 and E4 that resides in the same folder. E3 does not have a reference to E4. I am able to execute code in these assemblies as expected, so the references are OK. So far so good. Now, I have some code in E3 that tries to do some reflection on the types in E4. This fails.

代码如下:

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

dotnetPath is different from mainDir.
mytype is null. Expected result is a type-instance.
If I move the exe file to folder B, along with the .NET assemblies, it works. Then dotnetPath and mainDir is the same.
If I execute the reflection-code in E2 instead of E4, it works, even though dotnetPath != mainDir.
But in exactly the scenario I have outlines it does not work.

通过在配置文件中指定这些文件夹,我发现了一些将其他文件夹添加到 PrivateBinPath 中的 AppDomain 的技巧.但我没有成功.我试图将配置文件添加到我的 COM .NET 文件和我的 VB6 exe 文件,但我的 PrivateBinPath 属性没有发生任何变化.这是我尝试添加的配置文件:

I have found some tips on adding other folders to the AppDomain in the PrivateBinPath by specifying those in the config file. But I have not succeeded on this. I have tried to add a config file to my COM .NET file and my VB6 exe-file but nothing happened to my PrivateBinPath property. Here is the config file I tried to add:

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

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

Don't ask me to restructure my assemblies and types. The project is rather complicated and this is the best architecture.

推荐答案

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

I managed to solve this myself. The key was the AssemblyResolve-event that is fired if the system fails to resolve an assembly. It is explained here: http://msdn.microsoft.com/library/system.appdomain.assemblyresolve

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

It seems like a bug in the .NET framework that I have to use this event, but this workaround turned out to be fairly nice.

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);
}

事件处理程序相当简单:

The event handler is fairly simple:

Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
   foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
   {
      if (assembly.FullName == args.Name) return assembly;
   }
   return null;
}

这篇关于COM 互操作加载程序集时 AppDomain 的路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆