2016-12-05 1 views
0

Je tente de démarrer des applications via de nouvelles instances AppDomain dans mon processus. Cela fonctionne tout seul, mais je ne peux pas décharger l'AppDomain si j'ai démarré une application WPF (une CannotUnloadAppDomainException est levée si j'essaie). L'exécution d'applications de console ou d'applications WinForm, puis le déchargement de l'AppDomain fonctionnent correctement.Impossible de décharger un AppDomain après avoir exécuté l'application wpf

C'est le code que j'utilise pour configurer le code AppDomain et de déclenchement de la classe « InternalExecutableChecker »:

var manager = new AppDomainManager(); 
    var setup = new AppDomainSetup 
    { 
    ApplicationBase = Path.GetDirectoryName(executablePath),    
    LoaderOptimization = LoaderOptimization.MultiDomainHost 
    }; 
    AppDomain domain = manager.CreateDomain(Path.GetFileNameWithoutExtension(executablePath), null, setup); 
    try 
    { 
    domain.Load(Assembly.GetExecutingAssembly().FullName); 
    var checker = (InternalExecutableChecker)domain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(InternalExecutableChecker).FullName); 
    Logger.Info(checker.Check(executablePath)); 
    } 
    finally 
    { 
    AppDomain.Unload(domain);    
    } 

Voici le code de la classe InternalExecutableChecker qui est exécutée dans l'autre domaine (mise en place et démarrer un thread STA qui charge l'assembly à exécuter, le définit comme l'assembly d'entrée pour ce domaine, puis appelle la méthode d'entrée).

public class InternalExecutableChecker : MarshalByRefObject 
    { 
     private readonly object _lock = new object(); 
     private string _result; 

     public string Check(string executablePath) 
     { 
     var thread = new Thread(() => InternalCheck(executablePath)) { Name = AppDomain.CurrentDomain.FriendlyName }; 
     thread.SetApartmentState(ApartmentState.STA); 
     lock (_lock) 
     { 
      thread.Start(); 
      Monitor.Wait(_lock); 
     }   
     return _result; 
     } 

     private void InternalCheck(string executablePath) 
     { 
     try 
     { 
      Assembly assembly; 
      try 
      { 
       assembly = Assembly.LoadFrom(executablePath); 
      } 
      catch (BadImageFormatException) 
      { 
       _result = "No 32 bit .NET application"; 
       return; 
      }    
      try 
      { 
       ModifyEntryAssembly(assembly); 
       assembly.EntryPoint.Invoke(null, new object[] { }); 
      } 
      catch (Exception e) 
      { 
       _result = e.Message; 

      } 

      if (_result == null) 
      { 
       _result = "OK"; 
      } 
     } 
     finally 
     { 
      lock (_lock) 
      { 
       Monitor.Pulse(_lock); 
      } 
     }   
     } 

     private void ModifyEntryAssembly(Assembly assembly) 
     { 
     AppDomainManager manager = new AppDomainManager(); 
     FieldInfo entryAssemblyfield = manager.GetType().GetField("m_entryAssembly", BindingFlags.Instance | BindingFlags.NonPublic); 
     if (entryAssemblyfield == null) 
     { 
      throw new Exception("Could not retrieve entryAssemblyField."); 
     } 
     entryAssemblyfield.SetValue(manager, assembly); 

     AppDomain domain = AppDomain.CurrentDomain; 
     FieldInfo domainManagerField = domain.GetType().GetField("_domainManager", BindingFlags.Instance | BindingFlags.NonPublic); 
     if (domainManagerField == null) 
     { 
      throw new Exception("Could not retrieve domainManagerField."); 
     } 
     domainManagerField.SetValue(domain, manager); 
     } 
    } 

Répondre

1

Pour un domaine de déchargement correct avec Wpf App, vous devez l'arrêter. Par exemple:

CrossAppDomainDelegate action =() => 
    { 
     App app = null; 
     Thread thread = new Thread(() => 
     { 
      app = new App(); 
      app.MainWindow = new MainWindow(); 
      app.MainWindow.Show(); 
      app.Run(); 
     }); 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.Start(); 

     Task.Delay(TimeSpan.FromSeconds(1)).ContinueWith(_ => 
     { 
      app.Dispatcher.Invoke(()=>app.Shutdown()); 
     }); 
    }; 

Et quand:

domain.DoCallBack(action); 
... 
AppDomain.Unload(domain); 

Et note de MSDN:

Dans certains cas, l'appel Unload provoque une CannotUnloadAppDomainException immédiate, par exemple si elle est appelée dans un finaliseur .