好的,所以我有一个 MAF 应用程序,它在单独的 appdomain 中加载每个插件。这对我需要的东西非常有用,因为它允许我在运行时动态卸载和重新加载我的插件。
问题是,我需要能够在子 appdomain 中处理未处理的异常,捕获它,然后让该 appdomain 优雅地失败而不关闭父 appdomain
我该怎么做呢?这似乎是一项微不足道的任务,也是使用隔离应用程序域的主要好处之一......
据我所知,你不能开箱即用。您可以为插件视图使用抽象类,并结合模板方法模式在任何地方添加 try/catch 块(即,抽象插件视图中的 Read 方法调用插件应实现的虚拟 ReadCore 函数)。您仍然无法处理从子 AppDomain 的子线程抛出的未处理异常。这些将使您的应用程序崩溃。
同样,据我所知,有两种方法可以处理此问题:
System.AddIn
这种方法无法阻止您的应用程序崩溃,但您的应用程序会知道哪个加载项导致了崩溃。这是有价值的信息,因为这样您的应用程序可以禁用不稳定的加载项并避免在下次启动时加载它们。或者应用程序可以通知用户并让他/她决定做什么。请注意,这两者是互补的。您可以从 2. 开始,然后在不同的进程上加载不稳定的加载项。
最终采用一种今天看起来效果很好的方法,尽管在我发现这种方法的后果之前我还有一些工作要做......
以下博客文章帮助很大:http: //ikickandibite.blogspot.com/2010/04/appdomains-and-true-isolation.html
我只是在 app.config 中启用了遗留的未处理异常策略:
(作为仅供参考,即使在.Net 4.5中仍然可用,我对此感到担忧)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<runtime>
<legacyUnhandledExceptionPolicy enabled="true"/>
</runtime>
</configuration>
...从那时起,我可以使用 AppDomain.UnhandledException 事件来拦截未处理的异常并卸载子域...(请注意,我在这里松散地说“拦截”...我仍然没有“捕捉”例外,请注意在此过程中卸载插件)
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(IApplicationAddIn), pipelineStoreLocation, addInPath);
foreach (AddInToken token in tokens)
{
AppDomain domain = AppDomain.CreateDomain(token.Name);
domain.UnhandledException += (sender, args) =>
{
AppDomain _domain = (AppDomain) sender;
AppDomain.Unload(_domain);
};
Console.WriteLine("Initializing add-in '{0}'", token.Name);
IAddIn addin = token.Activate<IAddIn>(domain);
try
{
addin.Initialize(this);
}
catch (Exception ex)
{
Console.WriteLine("Problem initializing add-in '{0}': {1}", token.Name, ex.Message);
}
}