Lösa sammansättningsbelastningar

.NET tillhandahåller AppDomain.AssemblyResolve händelsen för program som kräver större kontroll över monteringsinläsningen. Genom att hantera den här händelsen kan ditt program läsa in en sammansättning i belastningskontexten utanför de normala avsökningssökvägarna, välja vilka av flera sammansättningsversioner som ska läsas in, generera en dynamisk sammansättning och returnera den och så vidare. Det här avsnittet innehåller vägledning för hantering av AssemblyResolve händelsen.

Anteckning

För att lösa sammansättningsbelastningar i kontexten endast reflektion använder du AppDomain.ReflectionOnlyAssemblyResolve händelsen i stället.

Så här fungerar AssemblyResolve-händelsen

När du registrerar en hanterare för AssemblyResolve händelsen anropas hanteraren när körningen inte binder till en sammansättning efter namn. Om du till exempel anropar följande metoder från användarkoden kan händelsen AssemblyResolve aktiveras:

Vad händelsehanteraren gör

Hanteraren för händelsen tar emot visningsnamnet för AssemblyResolve sammansättningen som ska läsas in i ResolveEventArgs.Name egenskapen . Om hanteraren inte känner igen sammansättningsnamnet returneras null (C#), Nothing (Visual Basic) eller nullptr (Visual C++).

Om hanteraren känner igen sammansättningsnamnet kan den läsa in och returnera en sammansättning som uppfyller begäran. I följande lista beskrivs några exempelscenarier.

  • Om hanteraren känner till platsen för en version av sammansättningen kan den läsa in sammansättningen med hjälp Assembly.LoadFrom av metoden eller Assembly.LoadFile och returnera den inlästa sammansättningen om den lyckas.

  • Om hanteraren har åtkomst till en databas med sammansättningar som lagras som bytematriser kan den läsa in en bytematris med hjälp av en av de Assembly.Load metodöverbelastningar som tar en bytematris.

  • Hanteraren kan generera en dynamisk sammansättning och returnera den.

Anteckning

Hanteraren måste läsa in sammansättningen i inläsningskontexten, i inläsningskontexten eller utan kontext. Om hanteraren läser in sammansättningen i kontexten för endast reflektion med hjälp Assembly.ReflectionOnlyLoad av metoden eller Assembly.ReflectionOnlyLoadFrom misslyckas belastningsförsöket AssemblyResolve som aktiverade händelsen.

Det är händelsehanterarens ansvar att returnera en lämplig sammansättning. Hanteraren kan parsa visningsnamnet för den begärda sammansättningen genom att skicka ResolveEventArgs.Name egenskapsvärdet till AssemblyName(String) konstruktorn. Från och med .NET Framework 4 kan hanteraren använda ResolveEventArgs.RequestingAssembly egenskapen för att avgöra om den aktuella begäran är beroende av en annan sammansättning. Den här informationen kan hjälpa dig att identifiera en sammansättning som uppfyller beroendet.

Händelsehanteraren kan returnera en annan version av sammansättningen än den version som begärdes.

I de flesta fall visas sammansättningen som returneras av hanteraren i belastningskontexten, oavsett vilken kontext hanteraren läser in den i. Om hanteraren till exempel använder Assembly.LoadFrom -metoden för att läsa in en sammansättning i inläsningskontexten visas sammansättningen i belastningskontexten när hanteraren returnerar den. I följande fall visas dock sammansättningen utan kontext när hanteraren returnerar den:

Information om kontexter finns i överlagring av Assembly.LoadFrom(String) metoden.

Flera versioner av samma sammansättning kan läsas in i samma programdomän. Den här metoden rekommenderas inte eftersom det kan leda till typtilldelningsproblem. Se Metodtips för monteringsinläsning.

Vad händelsehanteraren inte ska göra

Den primära regeln för hantering av AssemblyResolve händelsen är att du inte ska försöka returnera en sammansättning som du inte känner igen. När du skriver hanteraren bör du veta vilka sammansättningar som kan orsaka att händelsen aktiveras. Hanteraren bör returnera null för andra sammansättningar.

Viktigt

Från och med .NET Framework 4 AssemblyResolve aktiveras händelsen för satellitsammansättningar. Den här ändringen påverkar en händelsehanterare som har skrivits för en tidigare version av .NET Framework, om hanteraren försöker lösa alla begäranden om sammansättningsbelastning. Händelsehanterare som ignorerar sammansättningar som de inte känner igen påverkas inte av den här ändringen: De returnerar nulloch normala återställningsmekanismer följs.

Vid inläsning av en sammansättning får händelsehanteraren inte använda någon av de överlagringar av AppDomain.Load metoden eller Assembly.Load som kan orsaka AssemblyResolve att händelsen aktiveras rekursivt, eftersom detta kan leda till ett stackspill. (Se listan som angavs tidigare i det här avsnittet.) Detta inträffar även om du anger undantagshantering för belastningsbegäran, eftersom inget undantag genereras förrän alla händelsehanterare har returnerats. Därför resulterar följande kod i ett stackspill om MyAssembly inte hittas:

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

Rätt sätt att hantera AssemblyResolve

När du löser sammansättningar från AssemblyResolve händelsehanteraren genereras en StackOverflowException så småningom om hanteraren använder metodanropen Assembly.Load eller AppDomain.Load . Använd LoadFile i stället metoder eller LoadFrom eftersom de inte genererar AssemblyResolve händelsen.

Imagine som MyAssembly.dll finns nära den körande sammansättningen, på en känd plats, kan den lösas med hjälp av Assembly.LoadFile angiven sökväg till sammansättningen.

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

Se även