1

COM Interop 用に登録された .NET アセンブリが、アセンブリ内の型をロードするリフレクション コードと組み合わされると、奇妙な動作が発生します。デバッガーで何が起こっているのかを分析し、解決策を見つけるためにネットを検索しました。助けを借りて多くの記事を見つけましたが、問題を完全に解決するものはありませんでした.

問題の概要

Aには.NETではないexeファイルがあります(私の場合はVB6アプリケーション)。フォルダー A
にあります。フォルダー B に .NET dll がいくつかあります。そのうちの 1 つは 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 という 2 つの他の .NET アセンブリへの参照があります。E3 には E4 への参照がありません。これらのアセンブリで期待どおりにコードを実行できるので、参照は問題ありません.これまでのところ、私は E4 の型に何らかのリフレクションを行おうとするコードを E3 に持っています.これは失敗します.

コードは次のとおりです。

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 は同じです。
E4 ではなく E2 でリフレクション コードを実行すると、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>

アセンブリと型の再構築を求めないでください。プロジェクトはかなり複雑で、これが最適なアーキテクチャです。

4

1 に答える 1

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;
}
于 2012-04-24T11:34:51.123 に答える