Saturday, October 23, 2010

Exception Handling

Today’s post is about having “global” Exception handling at the Application Level. What I mean by that is handling an exception at the very "top" of an application, in your MainForm, in case exception handling as been missed by the developer in other modules, forms or whatever. Something like this will do it:

[STAThread]
static void Main(string[] args)
{
// Creates an instance of the methods that will handle the exception.
CustomExceptionHandler eh = new CustomExceptionHandler();

// Adds the event handler to to the event.
Application.ThreadException += new ThreadExceptionEventHandler(eh.OnThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

Application.Run(new MainForm());
}

// Creates a class to handle the exception event.
internal class CustomExceptionHandler
{
// Handles the exception event.
public void OnThreadException(object sender, ThreadExceptionEventArgs t)
{
DialogResult result = this.ShowThreadExceptionDialog(t.Exception);

// Exits the program after displaying message to the user.
// In Development mode, the developer will have more options (Abort/Retry/Ignore).
if (result == DialogResult.OK || result == DialogResult.Abort)
Application.Exit();
}

// Creates the error message and displays it.
private DialogResult ShowThreadExceptionDialog(Exception e)
{
DialogResult retval;
string msgUser = "An error occurred please contact the adminstrator with the following information:\n\n";
string msgDev = "An unhandled exception occurred (Abort/Retry/Ignore buttons are only displayed to Developers) \n\n";

string msgTrace = "Error: " + e.Message + "\n\n";
if (e.InnerException != null)
msgTrace += " " + e.InnerException.Message + "\n\n";
msgTrace += "Error Method: " + e.TargetSite + "\n\n" +
"Stack Trace: " + e.StackTrace;

if (System.Diagnostics.Debugger.IsAttached)
retval = MessageBox.Show(msgDev + msgTrace, "Application Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Exclamation);
else
retval = MessageBox.Show(msgUser + msgTrace, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);

return retval;
}
}

You'll notice that in my example, I only allow the user to Retry or Ignore if they're the developer and debugging. I assume that in such a case, the developer will want to see right off the bat what went wrong and where, but if this happens to a real user, it's typically NOT a good idea to allow them to continue, as it may easily lead to further corruption of data. You can also expand on this class to add error-logging or whatever you wish.

4 comments:

  1. Hey Bonnie,
    Thank you very much for this posting. Can this be initiated in the load event of an (mdi) load form?
    Kind regards,
    Marc Grajower

    ReplyDelete
  2. Hey Marc,

    That's a good question. And I don't have the answer! If I were to take a guess, I'd say no, because of the fact that the ThreadException is an event scoped to the Application, not to a specific form.

    You could try and play with it and see what you can come up with. Let me know if you try it ...

    ReplyDelete
  3. Well I can confirm with reasonable assurance that you can initiate a global error handler from the load event of an (mdi) form

    ReplyDelete
  4. Sorry for not replying sooner, Marc. I think I missed your Comment.

    Did it work correctly? Was this MDI Form your Main Form? Because if it wasn't, then it seems to me that when this MDI Form was not open, then you wouldn't have your global exception handler available to catch the exceptions. I think it would only work in the case where the MDI Form is your Main Form and is always open, right?

    ReplyDelete