Global try catch
Try-catches in each and single method is silly. But:
What is the reason your colleague wants you to catch exceptions to that extent? Do you let exceptions slip through to a level where they are unwanted?
I had a similar case with a product already in use with our customers. It was a WPF project that is similar to silverlight. My job was to ride out bugs in old bad code, that nobody still working with us mastered. The application cross-function with other programs in windows and it was impossible to foresee what could go wrong in different environments.
I had these problems:
- The program stopped working because of uncaught exceptions.
- It was hard to understand what went wrong, in order to fix the bugs. Our customers normally report errors by emailing screen dumps where it was hard to see what happened.
My approach was:
- Catching exceptions on selected "user and system end points". That is typically event handlers for button click, drag-n-drop, navigation commands, and so on from the user side, and typically windows messages and server responses from the system side.
- A class
OopsBox
to make the unexpected error handling a one-liner in each catch. Each catch has an as friendly message as possible, and hides the dirty stuff behind an expand button. The box is also used for error messages for expected errors, and in those cases there is no expand button and no dirty stuff to display as we know what went wrong already.
We gained this:
- Users had an easier time figuring out a workaround, as they were not thrown out of context, in cases when the error were not severe.
- It was, and still is, easier to grasp what went wrong when some unexpected behaviour was reported.
- The Oops boxes started out in large frequencies but I believe the product is stabilizing faster now, and the Oops-boxes are much rarer.
- Still to this day, when something goes wrong at a customer's, I get the call stack from them in an email. :)
It cost this:
- A large walk-through of all the user and system end points.
- Some logic had to be re-written to be able to put the catches at the right places.
Summary
Exceptions should be caught before they do any damage, like throwing the user out of context, and in a level where it makes sense.
When users run your program and something unexpected happens, make sure you have a way to point you to where to start looking. I did this by catching otherwise unhandled exceptions on "user and system end points" that I selected for this purpose.
Error box or not, try to find a way to not throw the user out of context when something goes wrong. It is hard to make it work in all cases though, but it is fatal when it happens.
You can capture unhanded (and thread) exceptions using the Application.ThreadException and AppDomain.CurrentDomain.UnhandledException properties.
Your Main would look something like this:
[STAThread]
static void Main() {
if (Debugger.IsAttached) {
Run();
return;
}
Application.ThreadException += ApplicationThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException;
Run();
}
Note the debugger check, just so the debugger can catch these exceptions when your developing.
The Run function is pretty simple
static void Run() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
And then the two exception handlers.
static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e) {
ErrorInformationDialog eid = new ErrorInformationDialog(e.Exception.Message, e.Exception);
eid.ShowDialog();
}
static void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) {
ErrorInformationDialog eid = new ErrorInformationDialog(e.ExceptionObject as Exception);
eid.ShowDialog();
}
And ErrorInformationDialog is just a form I put together to display an error notification and give instructions for reporting it.
you can always handle the AppDomain.UnhandledException Event