Delen via


Assemblybelastingen 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.

Notitie

Gebruik in plaats daarvan de AppDomain.ReflectionOnlyAssemblyResolve gebeurtenis voor het oplossen van assemblybelastingen 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 gebeurtenis-handler doet

De handler voor de AssemblyResolve gebeurtenis ontvangt de weergavenaam van de assembly die moet worden geladen, in de ResolveEventArgs.Name eigenschap. 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 bytematrices, kan deze een bytematrix laden met behulp van een van de Assembly.Load methode-overbelastingen die een bytematrix nemen.

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

Notitie

De handler moet de assembly laden in de load-from-context, in de belastingcontext of zonder context. Als de handler de assembly met behulp van de Assembly.ReflectionOnlyLoad of de Assembly.ReflectionOnlyLoadFrom methode in de context alleen weerspiegeling 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 context van de belasting te laden, wordt de assembly weergegeven in de laadcontext 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 aanbevolen procedures voor het laden van assembly's.

Wat de gebeurtenis-handler 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 ervoor kunnen zorgen dat de gebeurtenis wordt gegenereerd. Uw handler moet null retourneren voor andere assembly's.

Belangrijk

Vanaf .NET Framework 4 wordt de AssemblyResolve gebeurtenis gegenereerd voor satellietassembly's. Deze wijziging is van invloed op een gebeurtenishandler die is geschreven voor een eerdere versie van .NET Framework, als de handler alle aanvragen voor assemblybelastingen probeert op te lossen. Gebeurtenis-handlers die assembly's negeren die ze niet herkennen, worden niet beïnvloed door deze wijziging: ze retourneren nullen normale terugvalmechanismen worden gevolgd.

Bij het laden van een assembly mag de gebeurtenis-handler geen van de AppDomain.Load overbelastingen of Assembly.Load methoden gebruiken die ervoor kunnen zorgen dat de AssemblyResolve gebeurtenis recursief wordt gegenereerd, omdat dit kan leiden tot een stack-overloop. (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 gebeurtenis-handlers zijn geretourneerd. De volgende code resulteert dus in een stack-overloop als MyAssembly deze 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 omzetten van assembly's van de AssemblyResolve gebeurtenis-handler wordt er uiteindelijk een StackOverflowException gegenereerd als de handler gebruikmaakt van de Assembly.Load aanroepen of AppDomain.Load methode. LoadFile Gebruik of LoadFrom methoden, omdat ze de AssemblyResolve gebeurtenis niet genereren.

Stel dat deze MyAssembly.dll zich in de buurt van de uitvoerassembly bevindt, op een bekende locatie, kan worden omgezet met behulp van Assembly.LoadFile 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