在其中运行 wpf 应用程序后无法卸载 AppDomain

Posted

技术标签:

【中文标题】在其中运行 wpf 应用程序后无法卸载 AppDomain【英文标题】:Unable to unload an AppDomain after running wpf application in it 【发布时间】:2016-12-05 11:16:12 【问题描述】:

我正在尝试通过进程中的新 AppDomain 实例启动应用程序。这本身工作正常,但如果我启动 WPF 应用程序,我将无法卸载 AppDomain(如果尝试它,则会引发 CannotUnloadAppDomainException)。执行控制台应用程序或 WinForm 应用程序然后卸载 AppDomain 工作正常。

这是我用来设置“InternalExecutableChecker”类的 AppDomain 和触发代码的代码:

  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 应用程序正确卸载域,您必须关闭它。 例如:

    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 的注释:

在某些情况下,调用 Unload 会立即引发 CannotUnloadAppDomainException,例如在终结器中调用它时。

【讨论】:

以上是关于在其中运行 wpf 应用程序后无法卸载 AppDomain的主要内容,如果未能解决你的问题,请参考以下文章

安装UBUNTU时无法卸载/CDROM 挂载点

关于Inno Setup卸载程序删除文件夹的问题

在设备上卸载应用程序后,颤动运行不起作用

advanceinstaller打包安装后无法卸载

Windows 10 更新后VMware Workstation pro无法运行 (无需卸载原版本VM)

C# WPF WebBrowser控件调用Refresh()函数出现异常,导致程序“未响应”并无法继续运行