C# – Preventing Unhandled Exception Dialog Appearing

cexception handlingnet

First let me say I have read this useful article thoroughly and am using the SafeThread class from CodeProject. I get the same result whether using Thread or SafeThread.

I have reduced my problem down to an app consisting of two forms each with one button. The main program displays a form. When you click that button, a new thread starts which displays a second form. When you click the button on the second form, internally it just does "throw new Exception()"

When I run this under VS2008, I see "Exception in DoRun()".

When I run outside of VS2008, I get a dialog box "Unhandled exception has occurred in your application. If you click continue, the application …."

I have tried setting legacyUnhandledExceptionPolicy in the app.config to both 1 and 0.

What do I need to do to capture the exception thrown in my second form, when not running under VS2008?

Here's my Program.cs

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.ThreadException += new ThreadExceptionEventHandler    (Application_ThreadException);
            Application.SetUnhandledExceptionMode    (UnhandledExceptionMode.CatchException);
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            try
            {
               Application.Run(new Form1());
            }
            catch(Exception ex)
            {
                MessageBox.Show("Main exception");
            }                
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            MessageBox.Show("CurrentDomain_UnhandledException");
        }

        static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            MessageBox.Show("Application_ThreadException");
        }
    }

Here's Form1:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        SafeThread t = new SafeThread(new SimpleDelegate(ThreadMain));
        try
        {
            t.ShouldReportThreadAbort = true;
            t.ThreadException += new ThreadThrewExceptionHandler(t_ThreadException);
            t.ThreadCompleted += new ThreadCompletedHandler(t_ThreadCompleted);
            t.Start();
        }
        catch(Exception ex)
        {
            MessageBox.Show(string.Format("Caught externally! {0}", ex.Message));

        }
    }

    void t_ThreadCompleted(SafeThread thrd, bool hadException, Exception ex)
    {
        MessageBox.Show("t_ThreadCompleted");
    }

    void t_ThreadException(SafeThread thrd, Exception ex)
    {
        MessageBox.Show(string.Format("Caught in safe thread! {0}", ex.Message));
    }

    void ThreadMain()
    {
        try
        {
            DoRun();
        }
        catch (Exception ex)
        {
            MessageBox.Show(string.Format("Caught! {0}", ex.Message));
        }
    }

    private void DoRun()
    {
        try
        {
            Form2 f = new Form2();
            f.Show();
            while (!f.IsClosed)
            {
                Thread.Sleep(1);
                Application.DoEvents();
            }
        }
        catch(Exception ex)
        {
            MessageBox.Show("Exception in DoRun()");
        }
    }
}

And here is Form2:

public partial class Form2 : Form
{
    public bool IsClosed { get; private set; }

    public Form2()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        throw new Exception("INTERNAL EXCEPTION");
    }

    protected override void OnClosed(EventArgs e)
    {
        IsClosed = true;
    }
}

Best Answer

If you really want your second Form opened on a separate UI thread (not as ShowDialog()) to catch the exception and send it to your Application_ThreadException method, you need to ensure that the second thread is also set to CatchException and you need to subscribe to the Application.ThreadException on that thread, too. Both of these are thread-specific (and a bit quirky).

You can set the default "unhandled exception mode" by calling:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException, false);

This sets the application-wide mode to CatchException for any UI thread you create. (But this call will fail when running in the Visual Studio debugger and some other cases.) Or, your new UI thread can set its own mode with the usual call (same as passing true to this overload).

Either way, the new UI thread also needs to subscribe to the Application.ThreadException event itself, because the subscriber is stored in a [ThreadStatic] variable.

Application.ThreadException += Program.Application_ThreadException;

Or it could use a separate handler instead of routing to the same one, if that's helpful.

I'm not sure how this might intersect with using SafeThread to accomplish it, but I think if this is done correctly for the second UI thread it wouldn't be necessary to use SafeThread. It's much like you'd do it on your main UI thread.

Also see my answer to this question for more on the quirks of this stuff.

Related Topic