次の方法で共有


アセンブリ読み込みを解決する

.NET では、アセンブリの読み込みをより詳細に制御する必要があるアプリケーションに対して、 AppDomain.AssemblyResolve イベントが提供されます。 このイベントを処理することで、アプリケーションは、通常のプローブ パスの外部から読み込みコンテキストにアセンブリを読み込み、読み込むアセンブリバージョンを選択し、動的アセンブリを出力して返すことができます。 このトピックでは、 AssemblyResolve イベントを処理するためのガイダンスを提供します。

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

AssemblyResolve イベントのしくみ

AssemblyResolve イベントのハンドラーを登録すると、ランタイムが名前でアセンブリにバインドできないたびにハンドラーが呼び出されます。 たとえば、ユーザー コードから次のメソッドを呼び出すと、 AssemblyResolve イベントが発生する可能性があります。

イベント ハンドラーの機能

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

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

  • ハンドラーがアセンブリのバージョンの場所を認識している場合は、 Assembly.LoadFrom メソッドまたは Assembly.LoadFile メソッドを使用してアセンブリを読み込み、成功した場合は読み込まれたアセンブリを返すことができます。

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

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

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

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

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

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

  • ハンドラーは、コンテキストなしでアセンブリを読み込みます。

  • ResolveEventArgs.RequestingAssembly プロパティが null ではありません。

  • 要求するアセンブリ (つまり、 ResolveEventArgs.RequestingAssembly プロパティによって返されるアセンブリ) がコンテキストなしで読み込まれました。

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

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

イベント ハンドラーが行うべきでない操作

AssemblyResolve イベントを処理するための主な規則は、認識できないアセンブリを返そうとしてはいけないということです。 ハンドラーを記述するときは、イベントが発生する可能性があるアセンブリを把握しておく必要があります。 ハンドラーは、他のアセンブリに対して null を返す必要があります。

Von Bedeutung

.NET Framework 4 以降では、サテライト アセンブリに対して AssemblyResolve イベントが発生します。 この変更は、ハンドラーがすべてのアセンブリ読み込み要求を解決しようとした場合に、以前のバージョンの .NET Framework 用に書き込まれたイベント ハンドラーに影響します。 認識できないアセンブリを無視するイベント ハンドラーは、この変更の影響を受けず、 nullを返し、通常のフォールバック メカニズムに従います。

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

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);
        // DO NOT DO THIS: This causes a StackOverflowException
        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.
 */
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)
        // DO NOT DO THIS: This causes a StackOverflowException
        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 namespace System;
using namespace System::Reflection;

ref class Example
{
internal:
    static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
    {
        Console::WriteLine("Resolving {0}", e->Name);
        // DO NOT DO THIS: This causes a StackOverflowException
        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.
*/

AssemblyResolve を処理する正しい方法

アセンブリをAssemblyResolveイベントハンドラーから解決する際、ハンドラーがStackOverflowExceptionまたはAssembly.Loadメソッド呼び出しを使用すると、最終的にAppDomain.Loadがスローされることになります。 代わりに、LoadFile イベントが発生しないため、LoadFromまたはAssemblyResolveメソッドを使用します。

MyAssembly.dllが実行中のアセンブリの近くにあるとします。既知の場所では、アセンブリへのパスを指定Assembly.LoadFile使用して解決できます。

using System;
using System.IO;
using System.Reflection;

class CorrectExample
{
    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);

        var path = Path.GetFullPath("../../MyAssembly.dll");
        return Assembly.LoadFile(path);
     }
}
Imports System.IO
Imports System.Reflection

Class CorrectExample

    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)

        Dim fullPath = Path.GetFullPath("../../MyAssembly.dll")
        Return Assembly.LoadFile(fullPath)
    End Function
End Class
using namespace System;
using namespace System::IO;
using namespace System::Reflection;

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

        String^ fullPath = Path::GetFullPath("../../MyAssembly.dll");
        return Assembly::LoadFile(fullPath);
    }
};

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

こちらも参照ください