Update: Fixed the broken image link. No content changes.
When I'm writing console applications, I tend to put a try-catch block around everything in the Main loop, primarily to let me return a non-zero exit code from the application, which makes it play well with batch scripts. But it also makes it a good place to print error messages that pertain to well-known exception types. So I wind up with something like this:
static
int Main(string[] args) {
try {
// Do application logic here
}
catch (ParseCommandLineException pce) {
Console.WriteLine("Usage error");
return 27;
}
catch (Exception e) {
Console.WriteLine(e);
return 42;
}
}
When writing WinForms applications, returning a non-zero return code generally isn't necessary, since it's atypical to want to automate a GUI application using batch scripting methods. However, having a global exception filter is still a useful thing. For example, when FlexWikiPad encounters an unhandled exception, it captures the exception and displays a dialog box that gives the user the option of emailing the error message to me. This sort of feedback lets me know what error conditions users are running into most frequently, letting me fix the big PITA bugs first.
But how do you do it? Because Windows Forms applications are event-driven, there's not really a convenient place to put a top-level try-catch block. Indeed, if you try to do something like this in a simple WinForms application:
[STAThread]
static void Main() {
try {
Application.Run(new Form1());
}
catch (Exception e) {
MessageBox.Show("Error " + e.Message);
}
}
private void button1_Click(object sender, System.EventArgs e) {
throw new Exception("Whoops");
}
What you'll find is that when you run it, you get the following (probably familiar) window:

Note that you'll only see this error message if you run the app outside the debugger. Why? Because WinForms is making use of its own unhandled exception mechanism, and the default behavior differs based on whether you're debugging or not. This is slightly confusing, but worse it means that in some scenarios, your carefully-crafted catch block will never run.
Fortunately, you can override the default behavior, by hooking the Application object's ThreadException event, as in the following code:
[STAThread]
static void Main() {
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.Run(
new Form1());
}
private void button1_Click(object sender, System.EventArgs e) {
throw new Exception("Whoops");
}
private static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e) {
MessageBox.Show(e.Exception.Message);
}
Now when you run the application - inside or outside the debugger - your ThreadException handler will get called whenever an unhandled exception is thrown. You can do whatever you want here - exit the application gracefully, insult the user, or use a web service to log the bug in some sort of central database.
Posted
Jun 13 2004, 08:10 AM
by
craig-andera