G
Guest
Hi,
I'm trying to build a robust application framework that will be used by
other developers in the future. Their applications should be able to recover
themselves if they are crashing.
So, I thought, the best way to handle this is to lauch their stuff in a
separate AppDomain and when that AppDomain crashes, unload it and restart it.
This seems to work fine, unless an unhandled exception occurs in a
background thread that is started from that new AppDomain. Instead of just
that AppDomain crashing, the complete application is closed.
Does anyone know a way to prevent the application to close if an unhandled
exception occurs in a background thread that is started from another
AppDomain?
Any help is welcome,
Marc
Below is a test application that demonstrates what goes wrong. The goal is
to keep the application running while changing nothing in the Tester class,
because that is the part that will be written by other developers. I have no
control over them if they do something stupid like not handling any
exceptions in background threads.
<code>
using System;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace CrashRecoveryTest
{
internal class Program : MarshalByRefObject
{
private static void Main(string[] args)
{
try
{
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException +=
CurrentDomain_UnhandledException;
Console.WriteLine("Starting new AppDomain");
for (int i = 0; i < 3; i++)
{
AppDomain newDomain = AppDomain.CreateDomain("TestDomain");
newDomain.UnhandledException += newDomain_UnhandledException;
Console.WriteLine("Loading Tester");
Tester tester =
(Tester)newDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().GetName().Name, typeof(Tester).FullName);
Console.WriteLine("Calling Tester.Run method");
try
{
tester.Run();
}
catch (Exception ex)
{
Console.WriteLine("Caught exception " + ex.Message);
}
Console.WriteLine("Unloading AppDomain");
newDomain.UnhandledException -= newDomain_UnhandledException;
try
{
AppDomain.Unload(newDomain);
}
catch (Exception ex)
{
Console.WriteLine("Cannot unload AppDomain: " + ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred: "+ex.Message );
}
Console.WriteLine("Application ended ok");
}
private static void Application_ThreadException(object sender,
ThreadExceptionEventArgs e)
{
Console.WriteLine("Application.ThreadException occurred");
}
private static void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
Console.WriteLine("Unhandled exception occurred in default AppDomain");
}
private static void newDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
Console.WriteLine("Exception occurred in TestDomain");
}
}
public class Tester : MarshalByRefObject
{
public Tester()
{
Console.WriteLine("Tester is started in AppDomain " +
AppDomain.CurrentDomain.FriendlyName);
}
public void Run()
{
Console.WriteLine("Starting worker thread...");
Thread workerThread = new Thread(DoWork);
workerThread.Start();
Console.WriteLine("Doing some interesting work...");
for (int i = 0; i < 10; i++)
Thread.Sleep(1000);
workerThread.Join();
}
private static void DoWork()
{
Console.WriteLine("Worker thread is running in AppDomain "+
AppDomain.CurrentDomain.FriendlyName);
Thread.Sleep(3000);
Console.WriteLine("Worker thread about to throw an exception...");
throw new ApplicationException("Oops");
}
}
}
</code>
I'm trying to build a robust application framework that will be used by
other developers in the future. Their applications should be able to recover
themselves if they are crashing.
So, I thought, the best way to handle this is to lauch their stuff in a
separate AppDomain and when that AppDomain crashes, unload it and restart it.
This seems to work fine, unless an unhandled exception occurs in a
background thread that is started from that new AppDomain. Instead of just
that AppDomain crashing, the complete application is closed.
Does anyone know a way to prevent the application to close if an unhandled
exception occurs in a background thread that is started from another
AppDomain?
Any help is welcome,
Marc
Below is a test application that demonstrates what goes wrong. The goal is
to keep the application running while changing nothing in the Tester class,
because that is the part that will be written by other developers. I have no
control over them if they do something stupid like not handling any
exceptions in background threads.
<code>
using System;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
namespace CrashRecoveryTest
{
internal class Program : MarshalByRefObject
{
private static void Main(string[] args)
{
try
{
Application.ThreadException += Application_ThreadException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException +=
CurrentDomain_UnhandledException;
Console.WriteLine("Starting new AppDomain");
for (int i = 0; i < 3; i++)
{
AppDomain newDomain = AppDomain.CreateDomain("TestDomain");
newDomain.UnhandledException += newDomain_UnhandledException;
Console.WriteLine("Loading Tester");
Tester tester =
(Tester)newDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().GetName().Name, typeof(Tester).FullName);
Console.WriteLine("Calling Tester.Run method");
try
{
tester.Run();
}
catch (Exception ex)
{
Console.WriteLine("Caught exception " + ex.Message);
}
Console.WriteLine("Unloading AppDomain");
newDomain.UnhandledException -= newDomain_UnhandledException;
try
{
AppDomain.Unload(newDomain);
}
catch (Exception ex)
{
Console.WriteLine("Cannot unload AppDomain: " + ex.Message);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred: "+ex.Message );
}
Console.WriteLine("Application ended ok");
}
private static void Application_ThreadException(object sender,
ThreadExceptionEventArgs e)
{
Console.WriteLine("Application.ThreadException occurred");
}
private static void CurrentDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
Console.WriteLine("Unhandled exception occurred in default AppDomain");
}
private static void newDomain_UnhandledException(object sender,
UnhandledExceptionEventArgs e)
{
Console.WriteLine("Exception occurred in TestDomain");
}
}
public class Tester : MarshalByRefObject
{
public Tester()
{
Console.WriteLine("Tester is started in AppDomain " +
AppDomain.CurrentDomain.FriendlyName);
}
public void Run()
{
Console.WriteLine("Starting worker thread...");
Thread workerThread = new Thread(DoWork);
workerThread.Start();
Console.WriteLine("Doing some interesting work...");
for (int i = 0; i < 10; i++)
Thread.Sleep(1000);
workerThread.Join();
}
private static void DoWork()
{
Console.WriteLine("Worker thread is running in AppDomain "+
AppDomain.CurrentDomain.FriendlyName);
Thread.Sleep(3000);
Console.WriteLine("Worker thread about to throw an exception...");
throw new ApplicationException("Oops");
}
}
}
</code>