web-dev-qa-db-fra.com

Chargement/déchargement de l’assemblage dans différents domaines d’AppDomain

Je dois exécuter une méthode dans un assembly chargé pendant l'exécution. Maintenant, je veux décharger ces 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.

Les assemblys à charger sont des plugins dans mon framework de plugins. 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. L'ancien, 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, j'ai mis 5 cas.

  1. Si je configure le gestionnaire d'événements, j'obtiens une exception: le composant logiciel enfichable Assembly (c'est-à-dire une console de gestion (mmc.exe) n'a pas pu être trouvé/chargé.

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

  3. Je ne sais pas comment le type est nommé. Comment charger par interface?

  4. Semblable à 3. Comment obtenir le nom d'une assemblée?

  5. Même erreur que dans 1.

Je pense que le problème pourrait être la console de gestion d'une manière ou d'une autre ou je n'ai tout simplement aucune idée de ce que je fais mal. Toute aide est appréciée.

MISE À JOUR 1

J'ai maintenant essayé d'utiliser la solution proxy posté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 marche pas non plus. En essayant 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 chargé dans mmc (le composant logiciel enfichable). Une idée de comment résoudre cette erreur? AppDomain.AssemblyResolve n'est pas appelé (ni dans l'ancien ni dans le nouveau domaine).

MISE À JOUR 2

J'ai maintenant essayé la solution avec AppDomainSetup. Maintenant, l'exception a été modifiée en:

Impossible de charger le fichier ou le fichier Assembly: /// 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é n'était pas valide. (Exception de HRESULT: 0x80131047)

Une idée?

22
Scoregraphic

Essaye ça:

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 { }
13
BFree

Jetez un coup d'œil à cette réponse précédente: Comment charger un assemblage dans un autre AppDomain sur Windows Mobile (.NET CF)? . Cette réponse crée une classe proxy qui s'exécute dans un nouveau contexte AppDomain afin que vous puissiez avoir le contrôle total de votre initialisation.

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

1
Rubens Farias

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

Essayez donc d'appeler avec un nom complet, au lieu d'utiliser typeof(InstanceProxy).ToString(), utilisez string/text "<<Namespace>>.InstanceProxy".

comme ci-dessous

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