アセンブリ読み込みの解決

.NET Framework には、アセンブリの読み込みをより細かく制御する必要があるアプリケーション向けに AppDomain.AssemblyResolve イベントが用意されています。 このイベントを処理することにより、アプリケーションでは、アセンブリを通常のプローブ パス以外から読み込みコンテキストに読み込んだり、読み込むアセンブリのバージョンを複数選択したり、動的アセンブリを生成して返したりすることができます。 このトピックでは、AssemblyResolve イベントの処理に関するガイダンスを示します。

メモメモ

リフレクション専用コンテキストでアセンブリの読み込みを解決する場合は、代わりに AppDomain.ReflectionOnlyAssemblyResolve イベントを使用します。

AssemblyResolve イベントの動作

AssemblyResolve イベントのハンドラーを登録すると、ランタイムで名前によるアセンブリへのバインドに失敗した場合に、必ずこのハンドラーが呼び出されます。 たとえば、ユーザー コードから次のメソッドを呼び出すと、AssemblyResolve イベントが発生することがあります。

イベント ハンドラーで実行する処理

AssemblyResolve イベントのハンドラーは、読み込むアセンブリの表示名を ResolveEventArgs.Name プロパティで受け取ります。 アセンブリ名を認識できない場合、ハンドラーは null (Visual Basic の場合は Nothing、Visual C++ の場合は nullptr) を返します。

アセンブリ名を認識すると、ハンドラーは要求を満たすアセンブリを読み込んで返すことができます。 サンプルのシナリオをいくつか示します。

  • アセンブリのバージョンの場所がわかっている場合、Assembly.LoadFrom メソッドまたは Assembly.LoadFile メソッドを使用してアセンブリを読み込み、正常に読み込まれた場合はそのアセンブリを返すことができます。

  • アセンブリがバイト配列として格納されているデータベースにアクセスできる場合、バイト配列を受け取るいずれかの Assembly.Load メソッド オーバーロードを使用してバイト配列を読み込むことができます。

  • 動的アセンブリを生成して返すことができます。

メモメモ

このハンドラーでアセンブリを読み込むときは、読み込み元コンテキストまたは読み込みコンテキストに読み込むか、コンテキストなしで読み込む必要があります。Assembly.ReflectionOnlyLoad または Assembly.ReflectionOnlyLoadFrom メソッドを使用してリフレクション専用コンテキストにアセンブリを読み込もうとすると、AssemblyResolve イベントを発生させた読み込みは失敗します。

イベント ハンドラーは、適切なアセンブリを返す必要があります。 ハンドラーでは、ResolveEventArgs.Name プロパティの値を AssemblyName(String) コンストラクターに渡すことによって、要求されたアセンブリの表示名を解析できます。 .NET Framework Version 4 以降では、ResolveEventArgs.RequestingAssembly プロパティを使用して、現在の要求が別のアセンブリと依存関係にあるかどうかを確認できます。 この情報は、依存関係を満たすアセンブリを識別するうえで役立ちます。

イベント ハンドラーは、要求されたバージョンとは異なるバージョンのアセンブリを返すことがあります。

ほとんどの場合、ハンドラーによって返されるアセンブリは、ハンドラーが読み込んだコンテキストに関係なく、読み込みコンテキストで返されます。 たとえば、ハンドラーが Assembly.LoadFrom メソッドを使用して読み込み元コンテキストにアセンブリを読み込んだ場合、ハンドラーからアセンブリが返されるときは読み込みコンテキストで返されます。 ただし、次の場合は、アセンブリがハンドラーからコンテキストなしで返されます。

  • ハンドラーがコンテキストなしでアセンブリを読み込んだ場合。

  • ResolveEventArgs.RequestingAssembly プロパティが null でない場合。

  • 要求元のアセンブリ (ResolveEventArgs.RequestingAssembly プロパティによって返されるアセンブリ) がコンテキストなしで読み込まれている場合。

コンテキストの詳細については、Assembly.LoadFrom(String) メソッド オーバーロードのトピックを参照してください。

同じアセンブリの複数のバージョンを同じアプリケーション ドメインに読み込むこともできますが、 型の割り当てで問題が生じる可能性があるため、この方法はお勧めしません。 「アセンブリの読み込みのベスト プラクティス」を参照してください。

イベント ハンドラーで実行すべきでない処理

AssemblyResolve イベントを処理する際の基本的なルールとして、認識できないアセンブリは返さないようにしてください。 ハンドラーを記述するときは、このイベントが発生する原因となる可能性があるアセンブリを把握しておく必要があります。 それ以外のアセンブリについては、null を返すようにしてください。

重要 :重要

.NET Framework 4 以降では、AssemblyResolve イベントはサテライト アセンブリに対して発生します。この変更は、以前のバージョンの .NET Framework 用に記述されたイベント ハンドラーで、すべてのアセンブリ読み込み要求を解決しようとする場合に影響します。認識できないアセンブリを無視するイベント ハンドラーには、この変更による影響はありません。それらのイベント ハンドラーは null を返し、通常のフォールバック機構に従います。

イベント ハンドラーでアセンブリを読み込むときに、AssemblyResolve イベントを再帰的に発生させる可能性がある AppDomain.Load メソッド オーバーロードまたは Assembly.Load メソッド オーバーロードは使用しないでください。これを使用すると、スタック オーバーフローが発生する可能性があります (前述の一覧を参照してください)。 これは、読み込み要求の例外処理を指定した場合でも発生します。すべてのイベント ハンドラーから制御が戻るまでは例外がスローされないからです。 したがって、次のコードでは、MyAssembly が見つからないとスタック オーバーフローが発生します。

Imports System
Imports System.Reflection

Class BadExample

    Shared Sub Main()

        Dim ad As AppDomain = AppDomain.CreateDomain("Test")
        AddHandler ad.AssemblyResolve, AddressOf MyHandler

        Try
            Dim obj As object = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType")
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
    End Sub

    Shared Function MyHandler(ByVal source As Object, _
                              ByVal e As ResolveEventArgs) As Assembly
        Console.WriteLine("Resolving {0}", e.Name)
        Return Assembly.Load(e.Name)
    End Function
End Class

' This example produces output similar to the following:
'
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'...
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'
'Process is terminated due to StackOverflowException.
using System;
using System.Reflection;

class BadExample
{
    static void Main()
    {
        AppDomain ad = AppDomain.CreateDomain("Test");
        ad.AssemblyResolve += MyHandler;

        try
        {
            object obj = ad.CreateInstanceAndUnwrap(
                "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
                "MyType");
        } 
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    static Assembly MyHandler(object source, ResolveEventArgs e) 
    {
        Console.WriteLine("Resolving {0}", e.Name);
        return Assembly.Load(e.Name);
    }
} 

/* This example produces output similar to the following:

Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

Process is terminated due to StackOverflowException.
 */
using namespace System;
using namespace System::Reflection;

ref class Example
{
internal:
    static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e) 
    {
        Console::WriteLine("Resolving {0}", e->Name);
        return Assembly::Load(e->Name);
    }
};

void main()
{
    AppDomain^ ad = AppDomain::CreateDomain("Test");
    ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);

    try
    {
        Object^ obj = ad->CreateInstanceAndUnwrap(
            "MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
            "MyType");
    } 
    catch (Exception^ ex)
    {
        Console::WriteLine(ex->Message);
    }
}

/* This example produces output similar to the following:

Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null

Process is terminated due to StackOverflowException.
 */

参照

その他の技術情報

アセンブリの読み込みのベスト プラクティス

アプリケーション ドメインの使用