1

I want to find out how the Windows Event Log works when my app crashes, so I added a test button to throw new Exception(). To my surprise, the app just keeps running. Windows shows a dialog with the options to Continue or Quit, and clicking Continue, the app keeps running. I expected that the app would crash after continuing after an unhandled exception.

Most blogs on this topic test with a small console app that does a divide by zero, but I thought it does not add much trouble to create a small Forms app instead. In the real world I need to know how my Forms apps behave. Here is the code with two buttons: one to update a time display, to prove that the app is really running, and one to throw an unhandled exception.

using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            label_Msg.Text = DateTime.Now.ToString();
        }

        private void button_CrashNow_Click(object sender, EventArgs e)
        {
            throw new DivideByZeroException("This is Sandbox Crash Test");
            label_Msg.Text = DateTime.Now.ToString();
        }

        private void button_UpdateTime_Click(object sender, EventArgs e)
        {
            label_Msg.Text = DateTime.Now.ToString();
        }
    }
}

Running in the VS2017 debugger with F5 it will stop at the exception. Continuing with F5 would end the app.

But running it from the bin/debug folder, by double-clicking the .exe file, the app will NOT stop or quit, no matter how often I click the CrashNow button. By clicking the Update button, the time display updates as if no exception had happened.

The only thing the unhandled exception does is that the time update for that button does not work.

How is this possible?


By the way, my question is not about the difference between Application.ThreadException and AppDomain.CurrentDomain.UnhandledException. In fact, I have never heard of those exceptions. My question is also not about having to handle both of those exceptions. My question and that other question seem to mention the same Application.SetUnhandledExceptionMode() method. This surely is an interesting method. In my opinion, my question is not duplicate. However, a link to that other question may be useful for more in-depth understanding of what is going on under the WinForm hood.

Roland
  • 4,619
  • 7
  • 49
  • 81
  • @Çöđěxěŕ What is the difference between an uncaught exception and throwing a new one? – Roland May 08 '19 at 15:23
  • @Çöđěxěŕ Your code will not build, VS refuses to build dividing by zero.. Instead, I coded `double x = 2000 / (8 - DateTime.Today.Day);` . (Today is May 8) Result: same as `throw new Exception()` ... – Roland May 08 '19 at 15:33
  • If you really want to crash your application, write a function with faulty recursion. App definitely will crash for stackoverflowException :) – Vivek Natarajan May 08 '19 at 16:52
  • @Çöđěxěŕ Even simpler: `int x = 0; double y = 0 / x;` builds OK and throws a runtime exception – Roland May 09 '19 at 08:14
  • As you have discovered, in winforms the "unhandled" exception might be actually handled. But suppose you have a program in which the exception actually is unhandled. **The behavior of such programs is not specified by the C# language specification**. A conforming implementation is permitted to have any behavior in this situation, so you should not be surprised when an implementation has a particular behavior; every particular behavior is covered under "any behavior". – Eric Lippert May 09 '19 at 15:25
  • @EricLippert My C# book says that the program execution will terminate on an unhandled exception. Also that an error dialog is displayed. Of course by the OS. The WinForm default handler will NOT terminate the program, but offer an option to continue. Also, the def handler will catch the exception raised in the Form before the catch block in Program.cs. – Roland May 10 '19 at 10:17
  • @Roland: "terminate execution" and "display a dialog" are both behaviors, Since the behavior of a program with an unhandled exception is undefined, any behavior is permitted, including "terminate the program" and "display a dialog". I note that "terminate the program" and "display a dialog" are *opposites*; a terminated program cannot be displaying a dialog; it's terminated. – Eric Lippert May 10 '19 at 13:41
  • @EricLippert Now we go into detailed language... Termination is as far as I understand specified by the C# language, while you say that this is unspecified behaviour. The error dialog is shown by the OS after the app is terminated. This error dialog is not shown if the default WinForm exception handler catches the otherwise uncaught exception; that handler shows a different dialog with an option to continue, while the app is still running. Am I missing something? – Roland May 10 '19 at 15:28
  • @VivekNatarajan Sure. But if you have lots of RAM and the recursion eats memory slow, it might take a long time. The other methods discussed here are in my opinion simpler than cooking up a recursive algo, and will crash faster. – Roland May 10 '19 at 15:31
  • Well instead of arguing about it, why not read the specification? It says **If the search for matching catch clauses reaches the code that initially started the thread, then execution of the thread is terminated. The impact of such termination is implementation-defined.** Note that this specifies that the *thread* is terminated, not the *program*. Also note that there are special rules for unhandled exceptions which occur in destructors; see the spec for details. But the takeaway here is **unhandled exceptions are undefined behavior in C#**. – Eric Lippert May 10 '19 at 15:35
  • For example, here's a question: does "terminating the execution" of a thread run `finally` blocks? There are no matching `catch` blocks by assumption but there may be `finally` blocks; are you guaranteed that they run, or not? If they do, what if a finally block contains an infinite loop? How many times does that infinite loop run before the thread is terminated? – Eric Lippert May 10 '19 at 15:38
  • 1
    Now suppose the finally blocks do not run; suppose a finally block unlocks a lock that is blocking every other thread in the program. Do those threads ever unblock? Can an unhandled exception result in a program where every unterminated thread is blocked on a thread that does not exist, or is that impossible? – Eric Lippert May 10 '19 at 15:39
  • @EricLippert Excellent Question! But for me it is off-topic. My app has no locks and would not terminate after an 'unhandled' exception, and that problem is solved now. – Roland May 10 '19 at 15:51

1 Answers1

3

After reading the ms doc on the link suggested by the comment of steve16351 I learned that the Windows Forms app have a default exception handler.

By disabling that handler before creating the Form in Program.cs using:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

the unhandled exception will cause a different windows dialog, with options to

  • check online for a solution and close the program
  • close the program
  • debug the program

so now there is no way to continue with the Update button.

PS: I am sorry to see that he deleted his comment just before I wanted to upvote it


Update:

How silly this question may sound, it turned out that this setting is essential if you are thinking of catching all exceptions of your WinForms app with a try-catch block in Program.cs. Without this setting, exceptions in a Form are caught in such block only when running in the VS debugger, but not from a double-click on the *.exe in an Explorer window.

An exception thrown in a Form apparently is considered unhandled if it is not caught in a Form. You may try to catch it in the Program.cs that created the Form, but the default application handler may catch it first. Strange but true :-)

Roland
  • 4,619
  • 7
  • 49
  • 81