Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
.NET proporciona el evento AppDomain.AssemblyResolve para aplicaciones que requieren un mayor control sobre la carga de ensamblados. Al controlar este evento, la aplicación puede cargar un ensamblado en el contexto de carga desde fuera de las rutas de sondeo normales, seleccionar cuál de varias versiones de ensamblado cargar, generar y devolver un ensamblado dinámico, etc. En este tema se proporcionan instrucciones para controlar el AssemblyResolve evento.
Nota:
Para resolver cargas de ensamblado en el contexto de solo reflexión, use en su lugar el evento AppDomain.ReflectionOnlyAssemblyResolve.
Funcionamiento del evento AssemblyResolve
Al registrar un controlador para el evento AssemblyResolve, se invoca el controlador si se produce un error cuando el tiempo de ejecución enlaza a un ensamblado por nombre. Por ejemplo, llamar a los métodos siguientes desde el código de usuario puede provocar que se genere el AssemblyResolve evento:
Una sobrecarga del método AppDomain.Load o una sobrecarga del método Assembly.Load cuyo primer argumento es una cadena que representa el nombre para mostrar del ensamblado que se va a cargar (es decir, la cadena devuelta por la propiedad Assembly.FullName).
La sobrecarga de método AppDomain.Load o la sobrecarga de método Assembly.Load cuyo primer argumento es un objeto AssemblyName que identifica el ensamblado que se debe cargar.
Una sobrecarga del método Assembly.LoadWithPartialName.
Una sobrecarga de método AppDomain.CreateInstance o AppDomain.CreateInstanceAndUnwrap que crea una instancia de un objeto en otro dominio de aplicación.
Qué hace el controlador de eventos
El controlador del evento AssemblyResolve recibe el nombre para mostrar del ensamblado que se va a cargar, en la propiedad ResolveEventArgs.Name. Si el controlador no reconoce el nombre del ensamblado, devuelve null (C#), (Visual Basic) Nothing o nullptr (Visual C++).
Si el controlador reconoce el nombre del ensamblado, puede cargar y devolver un ensamblado que cumpla la solicitud. En la lista siguiente se describen algunos escenarios de ejemplo.
Si el controlador conoce la ubicación de una versión del ensamblado, puede cargar el ensamblado mediante el Assembly.LoadFrom método o Assembly.LoadFile y puede devolver el ensamblado cargado si se ejecuta correctamente.
Si el controlador tiene acceso a una base de datos de los ensamblados almacenados como matrices de bytes, puede cargar una matriz de bytes mediante una de las sobrecargas del método Assembly.Load que toman una matriz de bytes.
El controlador puede generar un ensamblado dinámico y devolverlo.
Nota:
El controlador debe cargar el ensamblado en el contexto de origen de carga, en el contexto de carga o sin contexto. Si el controlador carga el ensamblado en el contexto de solo reflexión mediante Assembly.ReflectionOnlyLoad o el método Assembly.ReflectionOnlyLoadFrom, el intento de carga que ha generado el evento AssemblyResolve sufre un error.
El controlador de eventos se encarga de devolver un ensamblado adecuado. Pasando el valor de propiedad ResolveEventArgs.Name al constructor AssemblyName(String), el controlador puede analizar el nombre visible del ensamblado solicitado. A partir de .NET Framework 4, el controlador puede usar la ResolveEventArgs.RequestingAssembly propiedad para determinar si la solicitud actual es una dependencia de otro ensamblado. Esta información puede ayudar a identificar un ensamblado que satisfaga la dependencia.
El controlador de eventos puede devolver una versión diferente del ensamblado que la versión solicitada.
En la mayoría de los casos, el ensamblado devuelto por el controlador aparece en el contexto de carga, independientemente del contexto en el que el controlador lo cargue. Por ejemplo, si el controlador usa el Assembly.LoadFrom método para cargar un ensamblado en el contexto load-from, el ensamblado aparece en el contexto de carga cuando el controlador lo devuelve. Sin embargo, en el siguiente caso, el ensamblado aparece sin contexto cuando el controlador lo devuelve:
El controlador carga un ensamblado sin contexto.
La ResolveEventArgs.RequestingAssembly propiedad no es null.
El ensamblado solicitante (es decir, el ensamblado devuelto por la propiedad ResolveEventArgs.RequestingAssembly) se ha cargado sin contexto.
Para obtener información sobre los contextos, vea la sobrecarga del método Assembly.LoadFrom(String).
Se pueden cargar varias versiones del mismo ensamblado en el mismo dominio de aplicación. Esta práctica no se recomienda, ya que puede provocar problemas de asignación de tipos. Consulte Procedimientos recomendados para la carga de ensamblados.
Qué no debe hacer el controlador de eventos
La regla principal que debe observar para controlar el evento AssemblyResolve es que no debe intentar devolver un ensamblado que no reconozca. Cuando se escribe el controlador, debe saber qué ensamblados pueden hacer que se produzca el evento. El controlador debe devolver NULL para otros ensamblados.
Importante
A partir de .NET Framework 4, se genera el evento AssemblyResolve para los ensamblados satélite. Este cambio afecta a un controlador de eventos que se escribió para una versión anterior de .NET Framework, si el controlador intenta resolver todas las solicitudes de carga de ensamblados. Los controladores de eventos que omiten los ensamblados que no reconocen no se ven afectados por este cambio: devuelven null y se siguen los mecanismos de recuperación normales.
Al cargar un ensamblado, el controlador de eventos no debe usar ninguna de las sobrecargas de método AppDomain.Load o Assembly.Load que pueden provocar que el AssemblyResolve evento se genere de forma recursiva, ya que esto puede provocar un desbordamiento de pila. (Consulte la lista proporcionada anteriormente en este tema). Esto sucede incluso si proporciona control de excepciones para la solicitud de carga, ya que no se produce ninguna excepción hasta que se hayan devuelto todos los controladores de eventos. Así pues, el código siguiente produce un desbordamiento de pila si no se encuentra MyAssembly:
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.
*/
La manera correcta de controlar AssemblyResolve
Al resolver ensamblados desde el controlador de eventos AssemblyResolve, al final se inicia una excepción StackOverflowException si el controlador usa las llamadas al método Assembly.Load o AppDomain.Load. En su lugar, use LoadFile o LoadFrom métodos, ya que no generan el AssemblyResolve evento.
Imagine que MyAssembly.dll se encuentra cerca del ensamblado en ejecución, en una ubicación conocida, se puede resolver mediante Assembly.LoadFile dada la ruta de acceso al ensamblado.
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);
}
}