This question comes up a lot in the forums. The first thing you need to ask yourself is if it is even necessary. The default
behavior for WinForms apps is to generate a nice exception dialog. Furthermore Windows will generate a crash dump that can then be loaded into the debugger
for diagnostic purposes. You are generally not gaining much by doing this yourself but we'll suppose for the moment it is necessary. The following program will
help demonstrate the concepts.
public class Form1 : Form
{
[STAThread]
static void Main ()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//Step1: Handle UI exceptions
Application.ThreadException += OnUIThreadException;
//Step2: Handle non-UI exceptions
AppDomain.CurrentDomain.UnhandledException += OnNonUIThreadException;
//Step3: Handle all UI exceptions
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
try
{
Application.Run(new Form1());
} catch (Exception e)
{
MessageBox.Show(e.Message, "In Main");
};
}
public Form1 ()
{
Button btnThrowUI = new Button();
Button btnThrowNonUI = new Button();
btnThrowUI.Location = new System.Drawing.Point(89, 67);
btnThrowUI.Name = "btnThrowUI";
btnThrowUI.Size = new System.Drawing.Size(85, 23);
btnThrowUI.TabIndex = 0;
btnThrowUI.Text = "Throw UI";
btnThrowUI.UseVisualStyleBackColor = true;
btnThrowUI.Click += new System.EventHandler(this.ThrowUI_Click);
btnThrowNonUI.Location = new System.Drawing.Point(89, 119);
btnThrowNonUI.Name = "btnThrowNonUI";
btnThrowNonUI.Size = new System.Drawing.Size(85, 23);
btnThrowNonUI.TabIndex = 1;
btnThrowNonUI.Text = "Throw Non-UI";
btnThrowNonUI.UseVisualStyleBackColor = true;
btnThrowNonUI.Click += new System.EventHandler(this.ThrowNonUI_Click);
ClientSize = new System.Drawing.Size(284, 264);
Controls.Add(btnThrowUI);
Controls.Add(btnThrowNonUI);
Name = "Form1";
Text = "Form1";
}
private void ThrowUI_Click ( object sender, EventArgs e )
{
throw new InvalidOperationException("UI thread");
}
private void ThrowNonUI_Click ( object sender, EventArgs e )
{
Thread thread = new Thread(DoWork);
thread.Start();
}
private void DoWork ()
{
throw new InvalidOperationException("Non-UI thread");
}
//Step1
private static void OnUIThreadException ( object sender, ThreadExceptionEventArgs e )
{
MessageBox.Show(e.Exception.Message, "Application.ThreadException");
}
//Step2
private static void OnNonUIThreadException ( object sender, UnhandledExceptionEventArgs
e )
{
Exception ex = e.ExceptionObject as Exception;
MessageBox.Show((ex != null) ? ex.Message : "Unknown exception",
"AppDomain.UnhandledException");
}
}
WinForms have an implicit try-catch wrapped around the call to Application.Run. Therefore any catch block you wrap around this method will
not be called. Instead you need to hook into the existing infrastructure. This is a multistep process: handle UI exceptions, handle non-UI exceptions, force processing
of all exceptions.
Handling UI exceptions involves working with Application as it is the class that is ultimately handling UI exceptions and displaying the exception
dialog. To handle UI thread exceptions subscribe and handle the Application.ThreadException event. This event is raised when an unhandled exception
on the UI thread occurs. Refer to the Step1 comments in the code.
The second step is to handle non-UI exceptions. Application does not deal with non-UI exceptions so you have to go back to the
appdomain. Handle AppDomain.UnhandledException to handle exceptions on non-UI threads. Refer to Step2 in the code.
The final step is to tell Application to handle all unhandled UI exceptions. This ensures that all the exceptions go to our handler. It requires
calling the Application.SetUnhandledExceptionMode method with the UnhandledExceptionMode.CatchException option. See step 3
A final warning. The above discussion is only about reacting to exceptions. In several situations, irrelevant of what you do, the application is still going
to terminate. Therefore you should generally not try to recover from the exception or reference anything that might throw another exception.