Okay, so I have a MAF application which loads up each addin inside of a separate appdomain. This is working fantastic for what I need as it allows me to dynamically unload and reload my addins at runtime.
The problem is, I need to be able to take an unhandled exception in the child appdomain, catch it, and then let that appdomain fail gracefully without taking down the parent appdomain
How do I go about doing this? It seems like a trivial task and one of the main benefits of using isolated appdomains...
I think my answer to this similar question should help you:
Application crash when anoter domain throws exception
Let me know if you need any more information.
As far as I know, you cannot do that out of the box. You could use abstract classes for the add-in views and add try/catch blocks everywhere combined with the Template Method pattern (i.e. the Read method in the abstract add-in view calls a virtual ReadCore function which the add-in should implement). Still you cannot handle unhandled exceptions thrown from child threads of the child AppDomain. These will crash your app.
Again as far as I know, there are two ways to handle this problem:
- Use process isolation. This is the only way to be sure that the add-ins will not crash the host.
- Follow the approach described in the Using AppDomain Isolation to Detect Add-In Failures article from the
System.AddIn
team's blog. This approach cannot stop your app from crashing but your app will know which add-in caused the crash. This is valuable information because then your app can disable the unstable add-ins and avoid loading them the next time it starts. Or the app can inform the user and let him/her decide what to do.
Note that these two are complementary. You can start with 2. and then load unstable add-ins on different processes.
Wound up going with an approach that appears to be working great today, though I still have some digging to do before I find out the ramifications of this approach...
The following blog post helped immensely:
http://ikickandibite.blogspot.com/2010/04/appdomains-and-true-isolation.html
I simply enabled the legacy unhandled exception policy in the app.config:
(which, as an FYI, is still available even in .Net 4.5, which I was concerned about)
<?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>
...From which point I can use the AppDomain.UnhandledException event to intercept an unhandled exception and unload the sub-domain...(note that I say 'intercept' here loosely...I'm still not 'catching' the exception, just taking note of it an unloading the addin in the process)
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);
}
}