c# - Montaje de carga/descarga en diferentes AppDomain
reflection mmc (3)
Eche un vistazo a esta respuesta anterior: cómo cargar un ensamblado en diferentes dominios de aplicaciones en Windows Mobile (.NET CF)? . Esa respuesta crea una clase de proxy que se ejecuta en el nuevo contexto AppDomain para que pueda tener un control total de su inicialización.
Podría crear un método Start()
en la clase ServiceApplicationProxy
y simplemente llamarlo normalmente desde su servidor con un proxy.Start()
.
Necesito ejecutar un método en un ensamblaje cargado durante el tiempo de ejecución. Ahora quiero descargar esos ensamblajes cargados después de la llamada al método. Sé que necesito un nuevo dominio de aplicación para poder descargar las bibliotecas. Pero aquí, el problema surge.
Los ensamblajes que se van a cargar son complementos en mi marco de plugins. No tienen punto de entrada en absoluto. Lo único que sé es que contienen algunos tipos que implementan una interfaz determinada. El antiguo código que no es AppDomain se ve así (ligeramente acortado):
try
{
string path = Path.GetFullPath("C:/library.dll");
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Assembly asm = Assembly.LoadFrom(path);
Type[] types = asm.GetExportedTypes();
foreach (Type t in types)
{
if ((t.GetInterface("IStarter") != null) && !t.IsAbstract)
{
object tempObj = Activator.CreateInstance(t);
MethodInfo info = t.GetMethod("GetParameters");
if (info != null)
{
return info.Invoke(tempObj, null) as string;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(String.Format("Damn ''{0}''.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("MyProject.View,"))
{
string path = Path.GetFullPath("C:/view.dll"));
return Assembly.LoadFrom(path);
}
return null;
}
Ahora quiero que carguen en un AppDomain propio:
try
{
string path = Path.GetFullPath("C:/library.dll");
AppDomain domain = AppDomain.CreateDomain("TempDomain");
domain.AssemblyResolve += CurrentDomain_AssemblyResolve; // 1. Exception here!!
domain.ExecuteAssembly(path); // 2. Exception here!!
domain.CreateInstanceFrom(...); // 3. I have NO clue, how the type is named.
domain.Load(...); // 4. I have NO clue, how the assembly is named.
domain.DoCallBack(...); // 5. Exception here!!
// ...
}
catch (Exception ex)
{
MessageBox.Show(String.Format("Damn ''{0}''.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Como puede ver, he puesto 5 casos.
Si configuro el controlador de eventos, recibo una excepción que indica que el ensamblado (es una consola de administración (mmc.exe) SnapIn. No se pudo encontrar / cargar.
ExecuteAssembly no encuentra un punto de entrada (bueno, no hay ninguno).
No tengo idea de cómo se llama el tipo. Cómo cargar por interfaz?
Similar a 3. ¿Cómo obtener el nombre de una asamblea?
Mismo error que en 1.
Creo que el problema podría ser la consola de gestión de alguna manera o simplemente no tengo idea de lo que estoy haciendo mal. Cualquier ayuda es apreciada.
ACTUALIZACIÓN 1
Ahora he intentado usar la solución de proxy publicada.
AppDomain domain = AppDomain.CreateDomain("TempDomain");
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(Assembly.GetAssembly(
typeof(InstanceProxy)).FullName, typeof(InstanceProxy).ToString()) as InstanceProxy;
if (proxy != null)
{
proxy.LoadAssembly(path);
}
AppDomain.Unload(domain);
public class InstanceProxy : MarshalByRefObject
{
public void LoadAssembly(string path)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Assembly asm = Assembly.LoadFrom(path);
Type[] types = asm.GetExportedTypes();
// ...see above...
}
}
Esto tampoco funciona. Al intentar crear el objeto proxy, obtengo una excepción:
No se pudo cargar el archivo o ensamblado ''MyProject.SnapIn, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null'' o una de sus dependencias. El sistema no puede encontrar el archivo especificado.
El archivo en el mensaje de error es el cargado en mmc (el SnapIn). ¿Alguna idea de cómo solucionar este error? AppDomain.AssemblyResolve no se llama (ni en el dominio antiguo ni en el nuevo).
ACTUALIZACIÓN 2
Ahora probé la solución con AppDomainSetup. Ahora, la excepción ha cambiado a:
No se pudo cargar el archivo o ensamblado ''file: /// C: /Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL'' o una de sus dependencias. El nombre de ensamblado dado o base de código no es válido. (Excepción de HRESULT: 0x80131047)
¿Alguna idea?
Prueba esto:
namespace SeperateAppDomainTest
{
class Program
{
static void Main(string[] args)
{
LoadAssembly();
}
public static void LoadAssembly()
{
string pathToDll = Assembly.GetExecutingAssembly().CodeBase;
AppDomainSetup domainSetup = new AppDomainSetup { PrivateBinPath = pathToDll };
var newDomain = AppDomain.CreateDomain("FooBar", null, domainSetup);
ProxyClass c = (ProxyClass)(newDomain.CreateInstanceFromAndUnwrap(pathToDll, typeof(ProxyClass).FullName));
Console.WriteLine(c == null);
Console.ReadKey(true);
}
}
public class ProxyClass : MarshalByRefObject { }
https://msdn.microsoft.com/en-us/library/3c4f1xde%28v=vs.110%29.aspx
especifica que
typeName Tipo: System.String
The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName
propiedad.
Por lo tanto, intente llamar con el nombre totalmente calificado, en lugar de usar typeof(InstanceProxy).ToString()
use string / text "<<Namespace>>.InstanceProxy"
como a continuación
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(path, "<<Namespace>>.InstanceProxy") as InstanceProxy;