Exception Handling in WPF
August 28, 2012 Leave a comment
There are 2 overriding principles since .NET 1.0, irrespective of platform:
- Only catch exceptions you plan to handle
- Only catch System.Exception in 1 place
- I whipped up a WinForm project and added a button to the default form. I then added the following code its code behind:
private void ThrowExceptionButton_Click(object sender, EventArgs e) { ArgumentOutOfRangeException exception = new ArgumentOutOfRangeException("New Exception"); throw exception; }
- When I run the app and press the button, I get this:
- Then, I added a general try…catch around the Application.Run method in Program.cs:
try { Application.Run(new Form1()); } catch (Exception exception) { MessageBox.Show("Exception occurred: " + exception.Message); }
And I got this:
All well and good – this has been the standard for years. I then decided to apply the same methodology to a WPF application.
The first thing I noticed is that there is not an Application.Run method. Rather, there is this:
<Application x:Class="Tff.ExceptionHandling.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml"> <Application.Resources> </Application.Resources> </Application>
Which means I can’t put that StartupUri in a general try…catch. Going over to MSDN, I see that you are supposed to use the Application.Current.DispatcherUnhandledException like so:
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { Application.Current.DispatcherUnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(Current_DispatcherUnhandledException); base.OnStartup(e); } void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { //log(e); e.Handled = true; } }
All well and good. If I run the app without debugging, the application handles the exception without shutting down. However if I run the application while debugging, I got this with or without the exception handling:
Which is different than WinForms…
So now that I have come to grips with WPF Exceptions on the mainUI thread, what about secondary threads?
In WinForms, I can do something like this:
BackgroundWorker backgroundWorker = null; public Form1() { backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork); InitializeComponent(); } void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { Thread.Sleep(1000); ArgumentOutOfRangeException exception = new ArgumentOutOfRangeException("New Exception"); throw exception; } private void ThrowExceptionOnSecondThreadButton_Click(object sender, EventArgs e) { backgroundWorker.RunWorkerAsync(); }
Sure enough:
But the important thing is this. If I start the application without debugging, then nothing happens when the secondary thread has an exception raised – it just ends silently. Another interesting offshoot of this is that when I add the global try..catch in Program – it doesn’t do anything for the background thread. Therefore, the debugger still stops when the exception is raised.
Hopping over to the WCF solution, the effect was exactly the same. If I am debugging, I get this:
But if I am not, the thread ends silently. As just like WinForms, that DispatcherUnhandledException doesn’t matter to the secondary thread.