2012-04-23 69 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; 
} 
相關問題