简体   繁体   English

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

[英]Unable to unload an AppDomain after running wpf application in it

I'm trying to start applications via new AppDomain instances inside my process. 我正在尝试通过流程中的新AppDomain实例启动应用程序。 This for itself works fine, but i'm unable to unload the AppDomain if i started a WPF application (a CannotUnloadAppDomainException is thrown if trying it). 这本身可以正常工作,但是如果我启动WPF应用程序,则无法卸载AppDomain(如果尝试运行它,则会抛出CannotUnloadAppDomainException)。 Executing console applications or WinForm applications and then unloading the AppDomain is working fine though. 但是,执行控制台应用程序或WinForm应用程序,然后卸载AppDomain仍然可以正常工作。

This is the code i use to setup the AppDomain and trigger code of the class "InternalExecutableChecker" : 这是我用来设置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);               
  }

This is the code of the InternalExecutableChecker class that is executed inside the other domain (setting up and starting an STA thread which loads the assembly to be executed, sets it as the entry assembly for this domain and then calls the entry method). 这是在其他域内执行的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);
      }
   }

For correct unload domain with Wpf App you must Shutdown it. 为了使用Wpf App正确卸载域,必须将其关闭。 For example: 例如:

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

And when: 什么时候:

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

And note from MSDN: 并从MSDN注意:

In some cases, calling Unload causes an immediate CannotUnloadAppDomainException, for example if it is called in a finalizer. 在某些情况下,调用Unload会立即导致CannotUnloadAppDomainException,例如,如果在终结器中调用了它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM