2012-04-23 160 views
1

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

概述问题

A的有一个exe文件,是不是.NET(在我的情况下,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") 

这造成我的.NET类的实例,它是registred为COM互操作。 .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其他两个.NET程序集的引用和E4位于同一个文件夹中 E3没有对E4的引用 我能够按照预期在这些程序集中执行代码,所以引用都是OK的 迄今为止很好 现在,我有一些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。
mytype为空。预期的结果是一个类型实例。
如果我将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 Framework中的错误,但这种解决办法被证明是相当不错。

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; 
} 
相关问题