2010-01-25 6 views
21

Je dois exécuter une méthode dans un ensemble chargé pendant l'exécution. Maintenant, je veux décharger les assemblys chargés après l'appel de la méthode. Je sais que j'ai besoin d'un nouvel AppDomain pour pouvoir décharger les bibliothèques. Mais ici, le problème se pose.Chargement/déchargement d'un ensemble dans un AppDomain différent

Les assemblages qui vont être chargés sont des plugins dans mon framework de plugin. Ils n'ont aucun point d'entrée du tout. Tout ce que je sais, c'est qu'ils contiennent des types qui implémentent une interface donnée. Le vieux, non-AppDomain code ressemble à ceci (légèrement raccourci):

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; 
} 

Maintenant, je veux qu'ils chargent dans un propre AppDomain:

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); 
} 

Comme vous pouvez le voir, je l'ai mis en 5 cas.

  1. Si je mets le gestionnaire d'événements, je reçois une exception que l'ensemble (il est une console de gestion (mmc.exe) SnapIn. N'a pas été trouvé/chargé.

  2. ExecuteAssembly ne trouve pas point d'entrée (bien, il n'y a pas).

  3. Je n'ai pas la moindre idée comment le type est nommé. Comment charger par l'interface?

  4. similaires à 3. Comment obtenir le nom d'un ensemble

    ?
  5. même erreur que dans 1.

Je pense que le problème pourrait être la console managment d'une façon ou j'ai juste aucune idée de ce que je fais mal. Toute aide est appréciée.

MISE À JOUR 1

J'ai maintenant essayé d'utiliser le proxy solution affichée.

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... 
    } 
} 

Cela ne fonctionne pas non plus. Lorsque j'essaie de créer l'objet proxy, j'obtiens une exception:

Impossible de charger le fichier ou l'assembly 'MyProject.SnapIn, Version = 1.0.0.0, Culture = neutre, PublicKeyToken = null' ou l'une de ses dépendances. Le système ne peut pas trouver le fichier spécifié.

Le fichier dans le message d'erreur est le sur chargé dans mmc (SnapIn). Une idée de comment corriger cette erreur? AppDomain.AssemblyResolve n'est pas appelé (ni dans l'ancien ou le nouveau domaine).

MISE À JOUR 2

J'ai maintenant essayé la solution avec le AppDomainSetup. Maintenant, l'exception a été remplacée par:

Impossible de charger le fichier ou l'assemblage 'file: /// C: /Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL' ou l'une de ses dépendances. Le nom d'assembly ou la base de code donné était invalide. (Exception de HRESULT: 0x80131047)

Une idée?

+1

Avez-vous essayé de créer une classe qui hérite de MarshallByRef à agir en tant que proxy et laissez-le faire ce sale boulot dans un nouveau contexte de domaine d'application? –

+0

Est-ce que cette classe serait dans l'assemblage à charger ou dans l'assemblage déjà chargé (celui qui essaie de charger les autres)? – Scoregraphic

+0

s'il vous plaît, voir ma réponse ci-dessous –

Répondre

11

Essayez ceci:

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

Merci pour l'indice avec AppDomainSetup, mais cela ne résout pas le problème. Voir ma question mise à jour – Scoregraphic

1

Regardez cette réponse précédente: How to load an assembly into different AppDomain on Windows Mobile (.NET CF) ?. Cette réponse crée une classe proxy qui s'exécute dans le nouveau contexte AppDomain afin que vous puissiez avoir le contrôle total de votre initialisation.

Vous pouvez créer une méthode Start() en classe ServiceApplicationProxy et l'appeler normalement depuis votre hébergeur avec un proxy.Start().

+0

Merci pour votre réponse. J'ai essayé, mais j'ai toujours des erreurs. Voir ma mise à jour dans la question originale. – Scoregraphic

0

https://msdn.microsoft.com/en-us/library/3c4f1xde%28v=vs.110%29.aspx

précise que

typeName Type: System.String

The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName 
propriété

.

Alors essayez d'appeler avec un nom entièrement qualifié, au lieu d'utiliser typeof(InstanceProxy).ToString() chaîne de l'utilisation/texte "<<Namespace>>.InstanceProxy"

comme ci-dessous

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

Est-ce que 'typeof (InstanceProxy) .FullName' ne serait pas meilleur que hardcoding l'espace de noms + nom de type? – Maarten

Questions connexes