2010-01-25 21 views
21

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.Montaje de carga/descarga en diferentes AppDomain

Los ensambles 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 viejo, no dominio de aplicación de código tiene este aspecto (ligeramente abreviada):

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 se carguen en un propio dominio de aplicación:

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 se puede ver, he puesto en 5 casos

  1. Si fijo el controlador de eventos, me sale una excepción que la asamblea (que es una consola de administración (mmc.exe) SnapIn. No se pudo encontrar/cargado.

  2. ExecuteAssembly no encuentra una punto de entrada (bueno, no hay ninguno).

  3. no tengo idea de cómo el tipo se llama. la forma de cargar por la interfaz?

  4. similares a 3. ¿Cómo obtener el nombre de un montaje?

  5. mismo error que en 1.

creo que el problema podría ser la consola de gerencia de alguna manera o tengo simplemente ni idea de lo que estoy haciendo mal. Cualquier ayuda es apreciada.

ACTUALIZA 1

ahora han intentado usar la solución de proxy publicado.

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 tratar de crear el objeto proxy, recibo 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 que está 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 han intentado la solución con el 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?

+1

¿Ha tratado de crear una clase que hereda de MarshallByRef para actuar como apoderado y dejar que haga ese trabajo sucio en el nuevo contexto de dominio de aplicación? –

+0

¿Estaría esta clase en el ensamblaje para cargar o en el ensamblaje ya cargado (el que intenta cargar los otros)? – Scoregraphic

+0

por favor, vea mi respuesta a continuación –

Respuesta

11

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 { } 
+0

Gracias por la sugerencia con AppDomainSetup, pero no resuelve el problema. Ver mi pregunta actualizada – Scoregraphic

1

Echa un vistazo a esta respuesta anterior: How to load an assembly into different AppDomain on 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.

Puede crear un método Start() en la clase ServiceApplicationProxy y simplemente llamarlo normalmente desde su hoster con un proxy.Start().

+0

Gracias por su respuesta. Intenté esto, pero sigo recibiendo errores. Ver mi actualización en la pregunta original. – Scoregraphic

0

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.

así que trata de llamar con nombre completo, en lugar de utilizar typeof(InstanceProxy).ToString() cadena de uso/texto "<<Namespace>>.InstanceProxy"

como a continuación

InstanceProxy proxy = domain.CreateInstanceAndUnwrap(path, "<<Namespace>>.InstanceProxy") as InstanceProxy; 
+0

¿No sería mejor 'typeof (InstanceProxy) .FullName' que hardcoding the namespace + typename? – Maarten