Resolver cargas de montagem

O .NET fornece o evento para aplicativos que exigem maior controle sobre o AppDomain.AssemblyResolve carregamento do assembly. Ao manipular esse evento, seu aplicativo pode carregar um assembly no contexto de carga de fora dos caminhos de sondagem normais, selecionar qual das várias versões de assembly carregar, emitir um assembly dinâmico e retorná-lo, e assim por diante. Este tópico fornece orientação para lidar com o AssemblyResolve evento.

Nota

Para resolver cargas de assembly no contexto somente reflexão, use o AppDomain.ReflectionOnlyAssemblyResolve evento em vez disso.

Como funciona o evento AssemblyResolve

Quando você registra um manipulador para o AssemblyResolve evento, o manipulador é invocado sempre que o tempo de execução não consegue se vincular a um assembly pelo nome. Por exemplo, chamar os seguintes métodos do código do usuário pode fazer com que o AssemblyResolve evento seja gerado:

O que o manipulador de eventos faz

O manipulador para o AssemblyResolve evento recebe o nome de exibição do assembly a ser carregado, na ResolveEventArgs.Name propriedade. Se o manipulador não reconhecer o nome do assembly, ele retornará null (C#), Nothing (Visual Basic) ou nullptr (Visual C++).

Se o manipulador reconhecer o nome do assembly, ele poderá carregar e retornar um assembly que satisfaça a solicitação. A lista a seguir descreve alguns cenários de exemplo.

  • Se o manipulador souber o local de uma versão do assembly, ele poderá carregá-lo usando o Assembly.LoadFrom método or Assembly.LoadFile e poderá retornar o assembly carregado se for bem-sucedido.

  • Se o manipulador tiver acesso a um banco de dados de assemblies armazenados como matrizes de bytes, ele poderá carregar uma matriz de bytes usando uma das sobrecargas de método que usam uma matriz de Assembly.Load bytes.

  • O manipulador pode gerar um assembly dinâmico e devolvê-lo.

Nota

O manipulador deve carregar o assembly no contexto load-from, no contexto load ou sem contexto. Se o manipulador carregar o assembly no contexto somente reflexão usando o Assembly.ReflectionOnlyLoad ou o Assembly.ReflectionOnlyLoadFrom método, a tentativa de carregamento que gerou o AssemblyResolve evento falhará.

É responsabilidade do manipulador de eventos retornar uma montagem adequada. O manipulador pode analisar o nome de exibição do assembly solicitado passando o valor da ResolveEventArgs.Name propriedade para o AssemblyName(String) construtor. A partir do .NET Framework 4, o manipulador pode usar a ResolveEventArgs.RequestingAssembly propriedade para determinar se a solicitação atual é uma dependência de outro assembly. Essas informações podem ajudar a identificar um assembly que satisfará a dependência.

O manipulador de eventos pode retornar uma versão diferente do assembly que a versão que foi solicitada.

Na maioria dos casos, o assembly retornado pelo manipulador aparece no contexto de carga, independentemente do contexto em que o manipulador o carrega. Por exemplo, se o manipulador usa o Assembly.LoadFrom método para carregar um assembly no contexto load-from, o assembly aparece no contexto de carga quando o manipulador o retorna. No entanto, no seguinte caso, o assembly aparece sem contexto quando o manipulador o retorna:

Para obter informações sobre contextos, consulte a sobrecarga de Assembly.LoadFrom(String) método.

Várias versões do mesmo assembly podem ser carregadas no mesmo domínio do aplicativo. Esta prática não é recomendada, porque pode levar a problemas de atribuição de tipo. Consulte Práticas recomendadas para carregamento de montagem.

O que o manipulador de eventos não deve fazer

A regra principal para manipular o AssemblyResolve evento é que você não deve tentar retornar um assembly que não reconhece. Ao escrever o manipulador, você deve saber quais assemblies podem fazer com que o evento seja gerado. Seu manipulador deve retornar null para outros assemblies.

Importante

Começando com o .NET Framework 4, o AssemblyResolve evento é gerado para assemblies satélite. Essa alteração afeta um manipulador de eventos que foi escrito para uma versão anterior do .NET Framework, se o manipulador tenta resolver todas as solicitações de carga de assembly. Os manipuladores de eventos que ignoram assemblies que não reconhecem não são afetados por essa alteração: eles retornam nulle os mecanismos de fallback normais são seguidos.

Ao carregar um assembly, o manipulador de eventos não deve usar nenhuma das sobrecargas de método ou Assembly.Load que podem fazer com que o AssemblyResolve evento seja gerado recursivamente, porque isso pode levar a um estouro de AppDomain.Load pilha. (Consulte a lista fornecida anteriormente neste tópico.) Isso acontece mesmo se você fornecer tratamento de exceção para a solicitação de carga, porque nenhuma exceção é lançada até que todos os manipuladores de eventos tenham retornado. Assim, o código a seguir resulta em um estouro de pilha se MyAssembly não for encontrado:

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

A maneira correta de lidar com AssemblyResolve

Ao resolver assemblies do AssemblyResolve manipulador de eventos, um StackOverflowException será eventualmente lançado se o manipulador usar as chamadas do Assembly.Load método or AppDomain.Load . Em vez disso, use LoadFile ou LoadFrom métodos, pois eles não geram o AssemblyResolve evento.

Imagine que MyAssembly.dll está localizado perto do assembly em execução, em um local conhecido, ele pode ser resolvido usando Assembly.LoadFile dado o caminho para o 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);
    }
}

Consulte também