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

时间:2016-12-05 11:16:13

标签: c# wpf appdomain

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

这是我用来设置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线程,该线程加载要执行的程序集,将其设置为该域的条目程序集,然后调用entry方法)

   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 个答案:

答案 0 :(得分: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的说明:

在某些情况下,调用Unload会立即导致CannotUnloadAppDomainException,例如,如果在终结器中调用它。