在运行wpf应用程序后无法卸载AppDomain
问题描述:
我试图通过我的进程中新的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);
}
}
答
对于使用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,例如,如果它被称为终结。