Delen via


Assemblyladen oplossen

.NET biedt de AppDomain.AssemblyResolve gebeurtenis voor toepassingen waarvoor meer controle nodig is over het laden van assembly's. Door deze gebeurtenis te verwerken, kan uw toepassing een assembly laden in de laadcontext van buiten de normale testpaden, selecteren welke van verschillende assemblyversies moeten worden geladen, een dynamische assembly verzenden en deze retourneren, enzovoort. Dit onderwerp bevat richtlijnen voor het afhandelen van de AssemblyResolve gebeurtenis.

Opmerking

Gebruik in plaats daarvan het AppDomain.ReflectionOnlyAssemblyResolve-evenement om assembly-loads op te lossen in de context die alleen voor reflectie geldt.

Hoe de AssemblyResolve-gebeurtenis werkt

Wanneer u een handler registreert voor de AssemblyResolve gebeurtenis, wordt de handler aangeroepen wanneer de runtime niet op naam aan een assembly kan worden gebonden. Als u bijvoorbeeld de volgende methoden aanroept vanuit gebruikerscode, kan dit ertoe leiden dat de AssemblyResolve gebeurtenis wordt gegenereerd:

Wat de event handler doet

De handler voor de AssemblyResolve gebeurtenis ontvangt in de ResolveEventArgs.Name eigenschap de weergavenaam van de assembly die moet worden geladen. Als de handler de assemblynaam niet herkent, wordt deze geretourneerd null (C#), Nothing (Visual Basic) of nullptr (Visual C++).

Als de handler de assemblynaam herkent, kan deze een assembly laden en retourneren die voldoet aan de aanvraag. In de volgende lijst worden enkele voorbeeldscenario's beschreven.

  • Als de handler de locatie van een versie van de assembly kent, kan de assembly worden geladen met behulp van de Assembly.LoadFrom of Assembly.LoadFile methode en kan de geladen assembly worden geretourneerd als dit lukt.

  • Als de handler toegang heeft tot een database met assembly's die zijn opgeslagen als byte-arrays, kan deze een byte-array laden met behulp van een van de Assembly.Load methode-overbelastingen die een byte-array als parameter accepteren.

  • De handler kan een dynamische assembly genereren en deze retourneren.

Opmerking

De handler moet de assembly laden in de load-from-context, in de load-context of zonder context. Als de handler de assembly met behulp van Assembly.ReflectionOnlyLoad of Assembly.ReflectionOnlyLoadFrom methode in de alleen-reflectiecontext laadt, mislukt de laadpoging die de AssemblyResolve gebeurtenis heeft veroorzaakt.

Het is de verantwoordelijkheid van de gebeurtenishandler om een geschikte assembly te retourneren. De handler kan de weergavenaam van de aangevraagde assembly parseren door de ResolveEventArgs.Name eigenschapswaarde door te geven aan de AssemblyName(String) constructor. Vanaf .NET Framework 4 kan de handler de ResolveEventArgs.RequestingAssembly eigenschap gebruiken om te bepalen of de huidige aanvraag afhankelijk is van een andere assembly. Deze informatie kan helpen bij het identificeren van een assembly die voldoet aan de afhankelijkheid.

De gebeurtenis-handler kan een andere versie van de assembly retourneren dan de versie die is aangevraagd.

In de meeste gevallen wordt de assembly die wordt geretourneerd door de handler weergegeven in de laadcontext, ongeacht de context waarin de handler deze laadt. Als de handler bijvoorbeeld de Assembly.LoadFrom methode gebruikt om een assembly in de load-from context te laden, wordt de assembly weergegeven in de load context wanneer de handler deze retourneert. In het volgende geval wordt de assembly echter zonder context weergegeven wanneer de handler deze retourneert:

Zie de overbelasting van de Assembly.LoadFrom(String) methode voor meer informatie over contexten.

Meerdere versies van dezelfde assembly kunnen in hetzelfde toepassingsdomein worden geladen. Deze procedure wordt niet aanbevolen, omdat dit kan leiden tot typetoewijzingsproblemen. Zie Beste praktijken voor het laden van assembly's.

Wat de eventhandler niet mag doen

De primaire regel voor het afhandelen van de AssemblyResolve gebeurtenis is dat u niet moet proberen een assembly te retourneren die u niet herkent. Wanneer u de handler schrijft, moet u weten welke assembly's de gebeurtenis kunnen oproepen. Uw handler moet null retourneren voor andere assembly's.

Belangrijk

Vanaf .NET Framework 4 wordt het AssemblyResolve-event gegenereerd voor satellietassemblies. Deze wijziging is van invloed op een gebeurtenishandler die is geschreven voor een eerdere versie van .NET Framework, als de handler alle assembly-laadverzoeken probeert af te handelen. Gebeurtenis-handlers die assemblies negeren die ze niet herkennen, worden niet beïnvloed door deze wijziging: ze retourneren null en normale terugvalmechanismen worden gevolgd.

Bij het laden van een assembly mag de event-handler geen van de AppDomain.Load of Assembly.Load method-overloads gebruiken die ervoor kunnen zorgen dat de AssemblyResolve gebeurtenis recursief wordt gegenereerd, omdat dit kan leiden tot een stack overflow. (Zie de lijst die eerder in dit onderwerp is opgegeven.) Dit gebeurt zelfs als u uitzonderingsafhandeling voor de laadaanvraag opgeeft, omdat er geen uitzondering wordt gegenereerd totdat alle event handlers zijn uitgevoerd. De volgende code resulteert in een stack-overloop als MyAssembly niet wordt gevonden:

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

De juiste manier om AssemblyResolve te verwerken

Bij het oplossen van assemblies vanuit de AssemblyResolve gebeurtenishandler zal er uiteindelijk een StackOverflowException optreden als de handler gebruikmaakt van de Assembly.Load- of AppDomain.Load-aanroepmethodes. In plaats daarvan, gebruik LoadFile of LoadFrom methoden, omdat ze de AssemblyResolve gebeurtenis niet genereren.

Stel dat MyAssembly.dll zich in de buurt van de uitvoerassembly bevindt, op een bekende locatie, zodat het kan worden opgelost met behulp van Assembly.LoadFile en het pad naar de assembly.

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

Zie ook