Sdílet prostřednictvím


Vyřešit načítání sestavení

.NET poskytuje AppDomain.AssemblyResolve událost pro aplikace, které vyžadují větší kontrolu nad načítáním sestavení. Díky zpracování této události může vaše aplikace načíst sestavení do kontextu zatížení mimo normální cesty sondy, vybrat, která z několika verzí sestavení se má načíst, vygenerovat dynamické sestavení a vrátit ho atd. Toto téma obsahuje pokyny pro zpracování AssemblyResolve události.

Poznámka:

Pro vyřešení načítání sestavení v reflexním kontextu použijte místo toho událost AppDomain.ReflectionOnlyAssemblyResolve.

Jak funguje událost AssemblyResolve

Při registraci obslužné rutiny pro AssemblyResolve událost se obslužná rutina vyvolá pokaždé, když se modulu runtime nezdaří navázat spojení se sestavením jeho názvem. Voláním následujících metod z uživatelského kódu může například dojít k vyvolání události AssemblyResolve:

Co dělá obslužná rutina události

Obslužná rutina události AssemblyResolve obdrží zobrazovaný název sestavení, který se má načíst, ve ResolveEventArgs.Name vlastnosti. Pokud obslužná rutina nerozpozná název sestavení, vrátí null (C#), Nothing (Visual Basic) nebo nullptr (Visual C++).

Pokud obslužná rutina rozpozná název sestavení, může načíst a vrátit sestavení, které splňuje požadavek. Následující seznam popisuje některé ukázkové scénáře.

  • Pokud obslužná rutina zná umístění verze sestavení, může sestavení načíst pomocí Assembly.LoadFrom metody nebo Assembly.LoadFile a může v případě úspěchu vrátit načtené sestavení.

  • Pokud má obslužná rutina přístup k databázi sestavení uložených jako pole bajtů, může načíst pole bajtů pomocí jednoho z Assembly.Load přetížení metody, které přebírají bajtové pole.

  • Obslužná rutina může vygenerovat dynamické sestavení a vrátit ho.

Poznámka:

Obslužná rutina musí načíst sestavení do kontextu načítání z kontextu, do načítacího kontextu nebo bez kontextu. Pokud obslužná rutina načte sestavení do kontextu pouze pro reflexi pomocí metody Assembly.ReflectionOnlyLoad nebo metody Assembly.ReflectionOnlyLoadFrom, pokus o načtení, který vyvolal událost AssemblyResolve, selže.

Je zodpovědností obslužného procesoru události vrátit vhodné sestavení. Obslužná rutina může analyzovat název sestavení zobrazeného na žádost předáním hodnoty vlastnosti ResolveEventArgs.Name do konstruktoru AssemblyName(String). Počínaje rozhraním .NET Framework 4 může obslužná rutina použít ResolveEventArgs.RequestingAssembly vlastnost k určení, zda aktuální požadavek je závislost jiného sestavení. Tyto informace můžou pomoct identifikovat sestavení, které bude vyhovovat závislosti.

Obslužná rutina události může vrátit jinou verzi sestavení, než je požadovaná verze.

Ve většině případů se sestavení vrácené obslužnou rutinou zobrazí v kontextu načítání, bez ohledu na to, do jakého kontextu ho obslužná rutina načte. Pokud například obslužná rutina používá metodu Assembly.LoadFrom k načtení sestavení do kontextu načtení z kontextu, sestavení se zobrazí v kontextu načtení, když obslužná rutina vrátí. Nicméně v následujícím případě se sestavení zobrazí bez kontextu, když jej obslužná rutina vrátí.

Informace o kontextech naleznete v Assembly.LoadFrom(String) přetížení metody.

Do stejné domény aplikace lze načíst více verzí stejného sestavení. Tento postup se nedoporučuje, protože může vést k problémům s přiřazením typu. Podívejte se na osvědčené postupy pro načítání sestavení.

Co by obslužná rutina události neměla vykonávat

Primárním pravidlem pro zpracování AssemblyResolve události je, že byste se neměli pokoušet vrátit sestavení, které nerozpoznáte. Při psaní obslužné rutiny byste měli vědět, která sestavení mohou způsobit vyvolání události. Vaše obslužná rutina by měla vracet hodnotu null pro ostatní sestavení.

Důležité

Počínaje rozhraním .NET Framework 4 AssemblyResolve je událost vyvolána pro satelitní sestavení. Tato změna má vliv na obslužnou rutinu události napsanou pro starší verzi rozhraní .NET Framework, pokud se obslužná rutina pokusí vyřešit všechny požadavky na načtení sestavení. Obslužné rutiny událostí, které ignorují sestavení, která nerozpoznávají, nejsou touto změnou ovlivněny: Vrátí nulla následují normální záložní mechanismy.

Při načítání sestavení nesmí obslužná rutina události používat žádné přetížení metod AppDomain.Load nebo Assembly.Load , které mohou způsobit, že událost AssemblyResolve bude vyvolána rekurzivně, protože to může vést k přetečení zásobníku. (Viz seznam uvedený výše v tomto tématu.) K tomu dochází, i když poskytujete zpracování výjimek pro požadavek na načtení, protože není vyvolána žádná výjimka, dokud všechny obslužné rutiny událostí nevrátí. Následující kód tedy vede k přetečení zásobníku, pokud MyAssembly ho nenajdete:

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

Správný způsob zpracování AssemblyResolve

Při řešení sestavení z obslužné rutiny AssemblyResolve bude nakonec vyvolána StackOverflowException výjimka, pokud obslužná rutina používá volání metod Assembly.Load nebo AppDomain.Load. Místo toho použijte LoadFile nebo LoadFrom metody, protože nevyvolají AssemblyResolve událost.

Představte si, že MyAssembly.dll se nachází v blízkosti sestavení, které je prováděno ve známém umístění, a lze jej určit pomocí Assembly.LoadFile při dané cestě k sestavení.

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

Viz také