2016-12-05 89 views
0

我试图通过我的进程中新的AppDomain实例启动应用程序。这本身工作正常,但我无法卸载AppDomain,如果我开始一个WPF应用程序(CannotUnloadAppDomainException抛出,如果尝试它)。执行控制台应用程序或WinForm应用程序,然后卸载AppDomain工作正常。在运行wpf应用程序后无法卸载AppDomain

这是我使用来设置类“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);    
    } 

这是其他域内所执行的InternalExecutableChecker类的代码(设定和启动加载要执行的程序集的STA线程,将其设置为该域的入口程序集,然后调用入口方法)。

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

回答

1

对于使用Wpf App正确卸载域,您必须关闭它。 例如:

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

当:

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

并注意从MSDN:

在某些情况下,调用卸载会导致立即CannotUnloadAppDomainException,例如,如果它被称为终结。