Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should use both AppDomain.UnhandledException and Application.DispatcherUnhandledException?

After reading some excellent posts about the difference between AppDomain.UnhandledException and Application.DispatcherUnhandledException, it appears that I should be handling both. This is because it is significantly more likely the user can recover from an exception thrown by the main UI thread (i.e., Application.DispatcherUnhandledException). Correct?

Also, should I also give the user a chance to continue the program for both, or just the Application.DispatcherUnhandledException?

Example code below handles both AppDomain.UnhandledException and Application.DispatcherUnhandledException, and both give the user the option to try to continue despite the exception.

[thanks and some of the code below is lifted from other answers]

App.xaml

<Application x:Class="MyProgram.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         Startup="App_StartupUriEventHandler"
         Exit="App_ExitEventHandler"
         DispatcherUnhandledException="AppUI_DispatcherUnhandledException">
    <Application.Resources>
    </Application.Resources>
</Application>

App.xaml.cs [redacted]

/// <summary>
/// Add dispatcher for Appdomain.UnhandledException
/// </summary>
public App()
    : base()
{
    this.Dispatcher.UnhandledException += OnDispatcherUnhandledException;
}

/// <summary>
/// Catch unhandled exceptions thrown on the main UI thread and allow 
/// option for user to continue program. 
/// The OnDispatcherUnhandledException method below for AppDomain.UnhandledException will handle all other exceptions thrown by any thread.
/// </summary>
void AppUI_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    if (e.Exception == null)
    {
        Application.Current.Shutdown();
        return;
    }
    string errorMessage = string.Format("An application error occurred. If this error occurs again there seems to be a serious bug in the application, and you better close it.\n\nError:{0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)", e.Exception.Message);
    //insert code to log exception here
    if (MessageBox.Show(errorMessage, "Application User Interface Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)
    {
        if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
        {
            Application.Current.Shutdown();
        }
    }
    e.Handled = true;
}

/// <summary>
/// Catch unhandled exceptions not thrown by the main UI thread.
/// The above AppUI_DispatcherUnhandledException method for DispatcherUnhandledException will only handle exceptions thrown by the main UI thread. 
/// Unhandled exceptions caught by this method typically terminate the runtime.
/// </summary>
void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
    string errorMessage = string.Format("An application error occurred. If this error occurs again there seems to be a serious bug in the application, and you better close it.\n\nError:{0}\n\nDo you want to continue?\n(if you click Yes you will continue with your work, if you click No the application will close)", e.Exception.Message);
    //insert code to log exception here
    if (MessageBox.Show(errorMessage, "Application UnhandledException Error", MessageBoxButton.YesNoCancel, MessageBoxImage.Error) == MessageBoxResult.No)
    {
        if (MessageBox.Show("WARNING: The application will close. Any changes will not be saved!\nDo you really want to close it?", "Close the application!", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning) == MessageBoxResult.Yes)
        {
            Application.Current.Shutdown();
        }
    }
    e.Handled = true;
}
like image 746
Kyle Avatar asked Apr 08 '12 07:04

Kyle


People also ask

Which event is used for unhandled exception?

The UnhandledException event is raised for unhandled exceptions thrown in other threads. Starting with Microsoft Visual Studio 2005, the Visual Basic application framework provides another event for unhandled exceptions in the main application thread.

What is DispatcherUnhandledException?

DispatcherUnhandledException is raised by an Application for each exception that is unhandled by code running on the main UI thread.

What does the FirstChanceException event allow within an ASP NET MVC application?

The FirstChanceException event of the AppDomain class lets you receive a notification that an exception has been thrown, before the common language runtime has begun searching for exception handlers. The event is raised at the application domain level.

How exception is handled globally in C#?

An ExceptionFilterAttribute is used to collect unhandled exceptions. You can register it as a global filter, and it will function as a global exception handler. Another option is to use a custom middleware designed to do nothing but catch unhandled exceptions.


2 Answers

  • AppDomain.CurrentDomain.UnhandledException in theory catches all exceptions on all threads of the appdomain. I found this to be very unreliable, though.
  • Application.Current.DispatcherUnhandledException catches all exceptions on the UI thread. This seems to work reliably, and will replace the AppDomain.CurrentDomain.UnhandledException handler on the UI thread (takes priority). Use e.Handled = true to keep the application running.

  • For catching exceptions on other threads (in the best case, they are handled on their own thread), I found System.Threading.Tasks.Task (only .NET 4.0 and above) to be low-maintenance. Handle exceptions in tasks with the method .ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted). See my answer here for details.

like image 166
Mike Fuchs Avatar answered Sep 22 '22 11:09

Mike Fuchs


An AppDomain.UnhandledException handler is wired as:

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 

But I could not find a way to flag as handled in the handler - so this always seems to result in an app shut down no matter what you do. So I do not think this is a lot of use.

Better to handle Application.Current.DispatcherUnhandledException and to test for CommunicationObjectFaultedException - as you can recover from that by just re-initializing your proxy - exactly as you did at initial connection. E.g:

void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) {     if (e.Exception is CommunicationObjectFaultedException) { //|| e.Exception is InvalidOperationException) {         Reconnect();         e.Handled = true;     }     else {         MessageBox.Show(string.Format("An unexpected error has occured:\n{0}.\nThe application will close.", e.Exception));         Application.Current.Shutdown();     } }  public bool Reconnect() {     bool ok = false;     MessageBoxResult result = MessageBox.Show("The connection to the server has been lost.  Try to reconnect?", "Connection lost", MessageBoxButton.YesNo);     if (result == MessageBoxResult.Yes)         ok = Initialize();     if (!ok)         Application.Current.Shutdown(); } 

where Initialize has your initial proxy instantiation/connection code.

In the code you posted above I suspect that you are handling DispatcherUnhandledException twice - by wiring a handler in xaml AND in code.

like image 44
Ricibob Avatar answered Sep 25 '22 11:09

Ricibob