Freigeben über


Auflösen von Assemblyladevorgängen

.NET stellt das AppDomain.AssemblyResolve Ereignis für Anwendungen bereit, die eine bessere Kontrolle über das Laden von Assemblys erfordern. Durch die Behandlung dieses Ereignisses kann Ihre Anwendung eine Assembly außerhalb der normalen Probingpfade in den Ladekontext laden, auswählen, welche von mehreren Assemblyversionen geladen werden sollen, eine dynamische Assembly ausgeben und zurückgeben usw. Dieses Thema enthält Anleitungen für die Behandlung des Ereignisses AssemblyResolve .

Hinweis

Zum Auflösen vom Laden einer Assembly in einem reflektionsbezogenen Kontext können Sie stattdessen das Ereignis AppDomain.ReflectionOnlyAssemblyResolve verwenden.

Funktionsweise des AssemblyResolve-Ereignisses

Wenn Sie einen Handler für das Ereignis AssemblyResolve registrieren, wird der Handler immer dann aufgerufen, wenn die Runtime eine Assembly nicht anhand deren Namen binden kann. Beispielsweise kann das Aufrufen der folgenden Methoden aus Benutzercode dazu führen, dass das AssemblyResolve Ereignis ausgelöst wird:

Funktionsweise des Ereignishandlers

Der Handler für das Ereignis AssemblyResolve erhält den Anzeigenamen der zu ladenden Assembly in der Eigenschaft ResolveEventArgs.Name. Wenn der Handler den Assemblynamen nicht erkennt, werden null (C#), Nothing (Visual Basic) oder nullptr (Visual C++) zurückgegeben.

Wenn der Handler den Assemblynamen erkennt, kann er eine Assembly laden und zurückgeben, die der Anforderung entspricht. In der folgenden Liste werden einige Beispielszenarien beschrieben.

  • Wenn der Handler den Speicherort einer Version der Assembly kennt, kann er die Assembly mithilfe der Assembly.LoadFrom-Methode oder der Assembly.LoadFile-Methode laden und die geladene Assembly zurückgeben, wenn dies erfolgreich ist.

  • Wenn der Handler Zugriff auf eine Datenbank von Assemblys hat, die als Byte-Arrays gespeichert sind, kann er mithilfe einer der Assembly.Load Methodenüberladungen, die ein Byte-Array verwenden, ein solches Array laden.

  • Der Handler kann eine dynamische Assembly generieren und zurückgeben.

Hinweis

Der Handler muss die Assembly in den LoadFrom-Kontext, in den Ladekontext oder ohne Kontext laden. Wenn der Handler die Assembly mithilfe der Assembly.ReflectionOnlyLoad Methode oder der Assembly.ReflectionOnlyLoadFrom Methode in den reinen Spiegelungskontext lädt, schlägt der Ladeversuch fehl, der das AssemblyResolve Ereignis ausgelöst hat.

Der Ereignishandler ist dafür verantwortlich, eine passende Assembly zurückzugeben. Der Handler kann den Anzeigenamen der angeforderten Assembly analysieren, indem der ResolveEventArgs.Name Eigenschaftswert an den AssemblyName(String) Konstruktor übergeben wird. Ab .NET Framework 4 kann der Handler die ResolveEventArgs.RequestingAssembly Eigenschaft verwenden, um zu bestimmen, ob es sich bei der aktuellen Anforderung um eine Abhängigkeit einer anderen Assembly handelt. Diese Informationen können dabei helfen, eine Assembly zu identifizieren, die die Abhängigkeit erfüllt.

Der Ereignishandler kann eine andere Version der Assembly zurückgeben als die angeforderte Version.

In den meisten Fällen erscheint die vom Handler zurückgegebene Assembly im Ladekontext, unabhängig davon, in welchen Kontext der Handler die Assembly lädt. Wenn der Handler beispielsweise die Assembly.LoadFrom Methode verwendet, um eine Assembly in den Kontext "load-from" zu laden, wird die Assembly im Ladekontext angezeigt, wenn der Handler sie zurückgibt. Im folgenden Fall wird die Assembly jedoch ohne Kontext angezeigt, wenn der Handler sie zurückgibt:

Weitere Informationen zu Kontexten finden Sie in der Methodenüberladung Assembly.LoadFrom(String).

Mehrere Versionen derselben Assembly können in dieselbe Anwendungsdomäne geladen werden. Diese Vorgehensweise wird nicht empfohlen, da sie zu Typzuweisungsproblemen führen kann. Weitere Informationen finden Sie unter Best Practices für das Laden von Assemblys.

Was der Ereignishandler nicht tun sollte

Die erste Regeln beim Behandeln des Ereignis AssemblyResolve ist, dass Sie nie versuchen sollten, eine Assembly zurückzugeben, die Sie nicht erkennen. Wenn Sie den Handler schreiben, sollten Sie wissen, welche Assemblys das Ereignis auslösen können. Ihr Handler sollte null für andere Assemblys zurückgeben.

Von Bedeutung

Ab .NET Framework 4 wird das Ereignis AssemblyResolve für Satellitenassemblys ausgelöst. Diese Änderung wirkt sich auf einen Ereignishandler aus, der für eine frühere Version von .NET Framework geschrieben wurde, wenn der Handler versucht, alle Assemblyladeanforderungen aufzulösen. Ereignishandler, die Assemblys ignorieren, die sie nicht erkennen, sind von dieser Änderung nicht betroffen: Sie geben zurück null, und normale Fallbackmechanismen werden befolgt.

Beim Laden einer Assembly darf der Ereignishandler nicht die Methodenüberladungen AppDomain.Load oder Assembly.Load verwendet, die dazu führen können, dass das Ereignis AssemblyResolve rekursiv ausgelöst wird, da dies zu einem Stapelüberlauf führen kann. (Weitere Informationen finden Sie in der weiter oben in diesem Thema angegebenen Liste.) Dies geschieht auch, wenn Sie eine Ausnahmebehandlung für die Ladeanforderung bereitstellen, da keine Ausnahme ausgelöst wird, bis alle Ereignishandler etwas zurückgegeben haben. Deshalb führt der folgende Code zu einem Stapelüberlauf, wenn MyAssembly nicht gefunden werden kann:

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.
*/

Die richtige Methode zum Behandeln von AssemblyResolve

Beim Auflösen von Assemblys aus dem AssemblyResolve-Ereignishandler wird schließlich eine StackOverflowException ausgelöst, wenn der Handler die Methodenaufrufe Assembly.Load oder AppDomain.Load verwendet. Verwenden Sie stattdessen LoadFile- oder LoadFrom-Methoden, da sie das AssemblyResolve-Ereignis nicht auslösen.

Stellen Sie sich vor, dass MyAssembly.dll sich in der Nähe der ausführenden Assembly an einem bekannten Speicherort befindet. Sie kann mithilfe von Assembly.LoadFile aufgelöst werden, vorausgesetzt der Pfad zur Assembly ist angegeben.

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

Siehe auch