|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Exception handling in AppDomain threadsI'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> Hello, Marc!
With best regards, Vadym Stetsiak. Blog: http://vadmyst.blogspot.com You wrote on Thu, 1 Nov 2007 12:45:00 -0700: MS> Hi, MS> I'm trying to build a robust application framework that will be used MS> by other developers in the future. Their applications should be MS> able to recover themselves if they are crashing. MS> So, I thought, the best way to handle this is to lauch their stuff MS> in a separate AppDomain and when that AppDomain crashes, unload it MS> and restart it. MS> This seems to work fine, unless an unhandled exception occurs in a MS> background thread that is started from that new AppDomain. Instead MS> of just that AppDomain crashing, the complete application is MS> closed. MS> Does anyone know a way to prevent the application to close if an MS> unhandled exception occurs in a background thread that is started MS> from another MS> AppDomain? MS> Any help is welcome, MS> Marc MS> Below is a test application that demonstrates what goes wrong. The MS> goal is to keep the application running while changing nothing in MS> the Tester class, because that is the part that will be written by MS> other developers. I have no control over them if they do something MS> stupid like not handling any exceptions in background threads. MS> <code> MS> using System; MS> using System.Reflection; MS> using System.Threading; MS> using System.Windows.Forms; MS> namespace CrashRecoveryTest MS> { MS> internal class Program : MarshalByRefObject MS> { MS> private static void Main(string[] args) MS> { MS> try MS> { MS> Application.ThreadException += Application_ThreadException; MS> Application.SetUnhandledExceptionMode(UnhandledExceptionMode. MS> CatchException); MS> AppDomain.CurrentDomain.UnhandledException += MS> CurrentDomain_UnhandledException; MS> Console.WriteLine("Starting new AppDomain"); MS> for (int i = 0; i < 3; i++) MS> { MS> AppDomain newDomain = AppDomain.CreateDomain("TestDomain"); MS> newDomain.UnhandledException += newDomain_UnhandledException; MS> Console.WriteLine("Loading Tester"); MS> Tester tester = MS> (Tester)newDomain.CreateInstanceAndUnwrap(Assembly. MS> GetExecutingAssembly().GetName().Name, typeof(Tester).FullName); MS> Console.WriteLine("Calling Tester.Run method"); MS> try MS> { MS> tester.Run(); MS> } MS> catch (Exception ex) MS> { MS> Console.WriteLine("Caught exception " + ex.Message); MS> } MS> Console.WriteLine("Unloading AppDomain"); MS> newDomain.UnhandledException -= newDomain_UnhandledException; MS> try MS> { MS> AppDomain.Unload(newDomain); MS> } MS> catch (Exception ex) MS> { MS> Console.WriteLine("Cannot unload AppDomain: " + ex.Message); MS> } MS> } MS> } MS> catch (Exception ex) MS> { MS> Console.WriteLine("Exception occurred: "+ex.Message ); MS> } MS> Console.WriteLine("Application ended ok"); MS> } MS> private static void Application_ThreadException(object sender, MS> ThreadExceptionEventArgs e) MS> { MS> Console.WriteLine("Application.ThreadException occurred"); MS> } MS> private static void CurrentDomain_UnhandledException(object sender, MS> UnhandledExceptionEventArgs e) MS> { MS> Console.WriteLine("Unhandled exception occurred in default MS> AppDomain"); MS> } MS> private static void newDomain_UnhandledException(object sender, MS> UnhandledExceptionEventArgs e) MS> { MS> Console.WriteLine("Exception occurred in TestDomain"); MS> } MS> } MS> public class Tester : MarshalByRefObject MS> { MS> public Tester() MS> { MS> Console.WriteLine("Tester is started in AppDomain " + MS> AppDomain.CurrentDomain.FriendlyName); MS> } MS> public void Run() MS> { MS> Console.WriteLine("Starting worker thread..."); MS> Thread workerThread = new Thread(DoWork); MS> workerThread.Start(); MS> Console.WriteLine("Doing some interesting work..."); MS> for (int i = 0; i < 10; i++) MS> Thread.Sleep(1000); MS> workerThread.Join(); MS> } MS> private static void DoWork() MS> { MS> Console.WriteLine("Worker thread is running in AppDomain "+ MS> AppDomain.CurrentDomain.FriendlyName); MS> Thread.Sleep(3000); MS> Console.WriteLine("Worker thread about to throw an exception..."); MS> throw new ApplicationException("Oops"); MS> } MS> } MS> } MS> </code> Hello, Marc!
Ignore previous message... The behavior you observe is by design (see http://msdn2.microsoft.com/en-us/library/ms228965.aspx ). If you want to recover from unhadled exceptions - your framework has to be in control of every operation that 3-d party code. Code must be run within try-catch blocks. However, swallowing exception is not good idea. IMO it is better to crash with a lot of noise then swallow exception and have inconsistent application state. -- With best regards, Vadym Stetsiak. Blog: http://vadmyst.blogspot.com You wrote on Thu, 1 Nov 2007 12:45:00 -0700: MS> Hi, MS> I'm trying to build a robust application framework that will be used MS> by other developers in the future. Their applications should be MS> able to recover themselves if they are crashing. MS> So, I thought, the best way to handle this is to lauch their stuff MS> in a separate AppDomain and when that AppDomain crashes, unload it MS> and restart it. MS> This seems to work fine, unless an unhandled exception occurs in a MS> background thread that is started from that new AppDomain. Instead MS> of just that AppDomain crashing, the complete application is MS> closed. MS> Does anyone know a way to prevent the application to close if an MS> unhandled exception occurs in a background thread that is started MS> from another MS> AppDomain? MS> Any help is welcome, MS> Marc MS> Below is a test application that demonstrates what goes wrong. The MS> goal is to keep the application running while changing nothing in MS> the Tester class, because that is the part that will be written by MS> other developers. I have no control over them if they do something MS> stupid like not handling any exceptions in background threads. MS> <code> MS> using System; MS> using System.Reflection; MS> using System.Threading; MS> using System.Windows.Forms; MS> namespace CrashRecoveryTest MS> { MS> internal class Program : MarshalByRefObject MS> { MS> private static void Main(string[] args) MS> { MS> try MS> { MS> Application.ThreadException += Application_ThreadException; MS> Application.SetUnhandledExceptionMode(UnhandledExceptionMode. MS> CatchException); MS> AppDomain.CurrentDomain.UnhandledException += MS> CurrentDomain_UnhandledException; MS> Console.WriteLine("Starting new AppDomain"); MS> for (int i = 0; i < 3; i++) MS> { MS> AppDomain newDomain = AppDomain.CreateDomain("TestDomain"); MS> newDomain.UnhandledException += newDomain_UnhandledException; MS> Console.WriteLine("Loading Tester"); MS> Tester tester = MS> (Tester)newDomain.CreateInstanceAndUnwrap(Assembly. MS> GetExecutingAssembly().GetName().Name, typeof(Tester).FullName); MS> Console.WriteLine("Calling Tester.Run method"); MS> try MS> { MS> tester.Run(); MS> } MS> catch (Exception ex) MS> { MS> Console.WriteLine("Caught exception " + ex.Message); MS> } MS> Console.WriteLine("Unloading AppDomain"); MS> newDomain.UnhandledException -= newDomain_UnhandledException; MS> try MS> { MS> AppDomain.Unload(newDomain); MS> } MS> catch (Exception ex) MS> { MS> Console.WriteLine("Cannot unload AppDomain: " + ex.Message); MS> } MS> } MS> } MS> catch (Exception ex) MS> { MS> Console.WriteLine("Exception occurred: "+ex.Message ); MS> } MS> Console.WriteLine("Application ended ok"); MS> } MS> private static void Application_ThreadException(object sender, MS> ThreadExceptionEventArgs e) MS> { MS> Console.WriteLine("Application.ThreadException occurred"); MS> } MS> private static void CurrentDomain_UnhandledException(object sender, MS> UnhandledExceptionEventArgs e) MS> { MS> Console.WriteLine("Unhandled exception occurred in default MS> AppDomain"); MS> } MS> private static void newDomain_UnhandledException(object sender, MS> UnhandledExceptionEventArgs e) MS> { MS> Console.WriteLine("Exception occurred in TestDomain"); MS> } MS> } MS> public class Tester : MarshalByRefObject MS> { MS> public Tester() MS> { MS> Console.WriteLine("Tester is started in AppDomain " + MS> AppDomain.CurrentDomain.FriendlyName); MS> } MS> public void Run() MS> { MS> Console.WriteLine("Starting worker thread..."); MS> Thread workerThread = new Thread(DoWork); MS> workerThread.Start(); MS> Console.WriteLine("Doing some interesting work..."); MS> for (int i = 0; i < 10; i++) MS> Thread.Sleep(1000); MS> workerThread.Join(); MS> } MS> private static void DoWork() MS> { MS> Console.WriteLine("Worker thread is running in AppDomain "+ MS> AppDomain.CurrentDomain.FriendlyName); MS> Thread.Sleep(3000); MS> Console.WriteLine("Worker thread about to throw an exception..."); MS> throw new ApplicationException("Oops"); MS> } MS> } MS> } MS> </code>
Show quote
"Vadym Stetsiak" wrote: Vladym, thanks for your quick response..> Hello, Marc! > > Ignore previous message... > > The behavior you observe is by design (see > http://msdn2.microsoft.com/en-us/library/ms228965.aspx ). > > If you want to recover from unhadled exceptions - your framework has to be > in control of every operation that 3-d party code. > Code must be run within try-catch blocks. > > However, swallowing exception is not good idea. IMO it is better to crash > with a lot of noise then swallow exception and have inconsistent application > state. > -- > With best regards, Vadym Stetsiak. > Blog: http://vadmyst.blogspot.com As I unload the AppDomain that created the thread and restart the whole thing all over again, I don't think you can call the application to be in an inconsistent state. All DLLs that were loaded are released from memory and loaded all over again. To me that is the same then restartin the application, no? I thought AppDomains were designed to avoid an application crashing completely when only a part of it fails... Marc |
|||||||||||||||||||||||