Best Practice for Exception Handling in a Windows Forms Application

Catch Application Exceptions in a Windows Forms Application

In Windows Forms applications, when an exception is thrown anywhere in the application (on the main thread or during asynchronous calls), you can catch it by registering for the ThreadException event on the Application. In this way you can treat all the exceptions in the same way.

Application.ThreadException += new ThreadExceptionEventHandler(MyCommonExceptionHandlingMethod);

private static void MyCommonExceptionHandlingMethod(object sender, ThreadExceptionEventArgs t)
{
//Exception handling...
}

How using try catch for exception handling is best practice

My exception-handling strategy is:

  • To catch all unhandled exceptions by hooking to the Application.ThreadException event, then decide:

    • For a UI application: to pop it to the user with an apology message (WinForms)
    • For a Service or a Console application: log it to a file (service or console)

Then I always enclose every piece of code that is run externally in try/catch :

  • All events fired by the WinForms infrastructure (Load, Click, SelectedChanged...)
  • All events fired by third party components

Then I enclose in 'try/catch'

  • All the operations that I know might not work all the time (IO operations, calculations with a potential zero division...). In such a case, I throw a new ApplicationException("custom message", innerException) to keep track of what really happened

Additionally, I try my best to sort exceptions correctly. There are exceptions which:

  • need to be shown to the user immediately

  • require some extra processing to put things together when they happen to avoid cascading problems (ie: put .EndUpdate in the finally section during a TreeView fill)

  • the user does not care, but it is important to know what happened. So I always log them:

  • In the event log

  • or in a .log file on the disk

It is a good practice to design some static methods to handle exceptions in the application top level error handlers.

I also force myself to try to:

  • Remember ALL exceptions are bubbled up to the top level. It is not necessary to put exception handlers everywhere.
  • Reusable or deep called functions does not need to display or log exceptions : they are either bubbled up automatically or rethrown with some custom messages in my exception handlers.

So finally:

Bad:

// DON'T DO THIS; ITS BAD
try
{
...
}
catch
{
// only air...
}

Useless:

// DON'T DO THIS; IT'S USELESS
try
{
...
}
catch(Exception ex)
{
throw ex;
}

Having a try finally without a catch is perfectly valid:

try
{
listView1.BeginUpdate();

// If an exception occurs in the following code, then the finally will be executed
// and the exception will be thrown
...
}
finally
{
// I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURRED OR NOT
listView1.EndUpdate();
}

What I do at the top level:

// i.e When the user clicks on a button
try
{
...
}
catch(Exception ex)
{
ex.Log(); // Log exception

-- OR --

ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

What I do in some called functions:

// Calculation module
try
{
...
}
catch(Exception ex)
{
// Add useful information to the exception
throw new ApplicationException("Something wrong happened in the calculation module:", ex);
}

// IO module
try
{
...
}
catch(Exception ex)
{
throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

There is a lot to do with exception handling (Custom Exceptions) but those rules that I try to keep in mind are enough for the simple applications I do.

Here is an example of extensions methods to handle caught exceptions a comfortable way. They are implemented in a way they can be chained together, and it is very easy to add your own caught exception processing.

// Usage:

try
{
// boom
}
catch(Exception ex)
{
// Only log exception
ex.Log();

-- OR --

// Only display exception
ex.Display();

-- OR --

// Log, then display exception
ex.Log().Display();

-- OR --

// Add some user-friendly message to an exception
new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
return ex;
}

Exception handling in WinForm

The proper technique really is the first one. If you can't handle it inside your function, then you have no business trying. Put the exception handling in a place where the exception can be handled and the program can continue (or exit gracefully) and the user be notified of the error in an appropriate fashion.

Exception handling behavior in WinForms application, which uses modal dialog

Try this in your Program.Main():

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
Application.Run(new Form1());
}

The reason is to do with the way Windows Forms assemblies are run outside of the Visual Studio Hosting process.
See this:

http://msdn.microsoft.com/en-us/library/system.windows.forms.application.threadexception.aspx

If you set the above line of code to:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

And run it in VS, you will see what the default behaviour is - the dialog box to which you refer will be shown, once you step over the initial exception in code. The default is simply different depending on whether you run in hosted or standalone mode.

Well, the "Why" is kind of covered in the MS link - the error occurs in an event handler, which is on a different thread. The default behaviour is to treat this differently, under WinForms only. If you put this line after your call to the dialog:

throw new Exception("Bah!");

And keep the behaviour on CatchException you will see that it goes to your exception handler as you would expect. It's only the exception in the event handler which is processed differently. Hope that helps.

What is the best practice for exception handling in silverlight?

For real logging that you could store & track, you will need to do it on the server, since you can't be guaranteed anything on the client will be persisted.

I would suggest exposing a "LogEvent(..)" method on a server side web service (maybe you already have one) which would then do the same kind of logging you do in ASP.net

Here's a video about basic web service calls in Silverlight if you haven't done that yet
http://silverlight.net/learn/learnvideo.aspx?video=66723

I'm not sure about any logging best practices though, my first guess would be to do the best practicies for logging in a web sevice on the server and expose that to the client.

Hope this helps!



Related Topics



Leave a reply



Submit