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:
Uma AppDomain.Load sobrecarga de método ou Assembly.Load sobrecarga de método cujo primeiro argumento é uma cadeia de caracteres que representa o nome de exibição do assembly a ser carregado (ou seja, a cadeia de caracteres retornada pela Assembly.FullName propriedade).
Uma AppDomain.Load sobrecarga de método ou Assembly.Load sobrecarga de método cujo primeiro argumento é um AssemblyName objeto que identifica o assembly a ser carregado.
Uma Assembly.LoadWithPartialName sobrecarga de método.
Uma AppDomain.CreateInstance sobrecarga de método ou AppDomain.CreateInstanceAndUnwrap que instancia um objeto em outro domínio de aplicativo.
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:
O manipulador carrega um assembly sem contexto.
A ResolveEventArgs.RequestingAssembly propriedade não é null.
O assembly solicitante (ou seja, o assembly que é retornado pela ResolveEventArgs.RequestingAssembly propriedade) foi carregado sem contexto.
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 null
e 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);
}
}