42

I can hookup to AppDomain.CurrentDomain.UnhandledException to log exceptions from background threads, but how do I prevent them terminating the runtime?

Richard Ev
  • 52,939
  • 59
  • 191
  • 278
luntain
  • 4,560
  • 6
  • 37
  • 48

5 Answers5

44

First, you really should try not to have exceptions thrown - and not handled - in a background thread. If you control the way your delegate is run, encapsulate it in a try catch block and figure a way to pass the exception information back to your main thread (using EndInvoke if you explicitly called BeginInvoke, or by updating some shared state somewhere).

Ignoring a unhandled exception can be dangerous. If you have a real un-handlable exception (OutOfMemoryException comes into mind), there's not much you can do anyway and your process is basically doomed.

Back to .Net 1.1, an unhandled exception in a backgroundthread would just be thrown to nowhere and the main thread would gladly plough on. And that could have nasty repercussions. So in .Net 2.0 this behavior has changed.

Now, an unhandled exception thrown in a thread which is not the main thread will terminate the process. You may be notified of this (by subscribing to the event on the AppDomain) but the process will die nonetheless.

Since this can be inconvenient (when you don't know what will be run in the thread and you are not absolutely sure it's properly guarded, and your main thread must be resilient), there's a workaround. It's intended as a legacy settings (meaning, it's strongly suggested you make sure you don't have stray threads) but you can force the former behavior this way :

Just add this setting to your service/application/whatever configuration file :

<configuration>
  <runtime>
    <!-- the following setting prevents the host from closing when an unhandled exception is thrown -->
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

It doesn't seem to work with ASP.NET, though.

For more information (and a huge warning that this setting may not be supported in upcoming versions of the CLR) see http://msdn.microsoft.com/en-us/library/ms228965.aspx

Frederik Struck-Schøning
  • 12,981
  • 8
  • 59
  • 68
  • 1
    having try...catchs all over the place stinks. I woudl want just one in the unhandled location AND would not want my app to die as a simple nullpointer just kept happening when a user went to click "feature X" and we told them to use the other feature X button which was working...it didn't shut the app down every time which annoys the user just as much as the original bug annoyed them.(booting an app takes time so it adds annoyance here to users) – Dean Hiller Dec 30 '11 at 18:31
  • 1
    I am having problems with a buggy proprietary external library which erratically crashes the application with an (internal) `ArgumentNullException`. I would like to enable `legacyUnhandledExceptionPolicy` just for that library. Can this be done? – chris May 14 '15 at 11:15
10

From Joe Albahari's excellent threading article:

The .NET framework provides a lower-level event for global exception handling: AppDomain.UnhandledException. This event fires when there's an unhandled exception in any thread, and in any type of application (with or without a user interface). However, while it offers a good last-resort mechanism for logging untrapped exceptions, it provides no means of preventing the application from shutting down – and no means to suppress the .NET unhandled exception dialog.

In production applications, explicit exception handling is required on all thread entry methods. One can cut the work by using a wrapper or helper class to perform the job, such as BackgroundWorker (discussed in Part 3).

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
6

Keeping the answer short, yes, you do can prevent the runtime from terminating.

Here is a demo of the workaround:

class Program
{
    void Run()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Console.WriteLine("Press enter to exit.");

        do
        {
            (new Thread(delegate()
            {
                throw new ArgumentException("ha-ha");
            })).Start();

        } while (Console.ReadLine().Trim().ToLowerInvariant() == "x");


        Console.WriteLine("last good-bye");
    }

    int r = 0;

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Interlocked.Increment(ref r);
        Console.WriteLine("handled. {0}", r);
        Console.WriteLine("Terminating " + e.IsTerminating.ToString());

        Thread.CurrentThread.IsBackground = true;
        Thread.CurrentThread.Name = "Dead thread";            

        while (true)
            Thread.Sleep(TimeSpan.FromHours(1));
        //Process.GetCurrentProcess().Kill();
    }

    static void Main(string[] args)
    {
        Console.WriteLine("...");
        (new Program()).Run();
    }
}

Essentially, you just did not let the runtime to show the "... program has stopped working" dialog.

In case you need to log the exception and silently exit, you can call Process.GetCurrentProcess().Kill();

bohdan_trotsenko
  • 5,167
  • 3
  • 43
  • 70
  • 2
    ick. now my thread is hung and stuck forever and more and more threads get hung. I would rather have a way like java to keep on ploughing along...we had some exceptions that were not disastrous in java and the app kept on running and we told users to just avoid the corner case steps of 1,2,3. This seems bad that C# doesn't support this well. – Dean Hiller Dec 30 '11 at 18:29
  • 1
    Yes, this is a dirty trick, by the way. Threads are supposed to handle all the exceptions. – bohdan_trotsenko Jan 02 '12 at 18:48
  • 3
    this is just sweeping the dirt under the rug, a very bad idea – Weipeng Jul 18 '18 at 02:55
  • 1
    This is a good way of terminating the UI of the app gracefully, which is impossible otherwise. – Ed Bayiates Aug 05 '22 at 21:22
3
    AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
    {
        Thread.CurrentThread.Join();
    };

But be careful, this code will freeze all the stack memory of the Thread and thread's managed object itself. However, if your application is in determined state (may be you threw LimitedDemoFunctionalityException or OperationStopWithUserMessageException) and you are not developing 24/7 application this trick will work.

Finally, I think MS should allow developers to override the logic of unhandled exceptions from the top of the stack.

Evgeny Gorbovoy
  • 765
  • 3
  • 20
  • 1
    Doing this is a terrible idea, but this answer didn't deserve to be downvoted. Given that Microsoft doesn't provide any 'safe' way of handling this, there's nothing else you can do if the process-killing exception is being generated in third-part code that you can't fix. – Ashley Oct 12 '19 at 16:28
1

Here is a great blog post about this problem: Handling "Unhandled Exceptions" in .NET 2.0

IMO it would be right to handle exceptions in background threads manually and re-throw them via callback if necessary.

delegate void ExceptionCallback(Exception ex);

void MyExceptionCallback(Exception ex)
{
   throw ex; // Handle/re-throw if necessary
}

void BackgroundThreadProc(Object obj)
{
   try 
   { 
     throw new Exception(); 
   }
   catch (Exception ex)
   { 
     this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex); 
   }
}

private void Test()
{
   ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc));
}
aku
  • 122,288
  • 32
  • 173
  • 203