27

Después de leer algunas publicaciones excelentes sobre la diferencia entre AppDomain.UnhandledException y Application.DispatcherUnhandledException, parece que debería manejar ambos. Esto se debe a que es significativamente más probable que el usuario pueda recuperarse de una excepción lanzada por el subproceso principal de la interfaz de usuario (es decir, Application.DispatcherUnhandledException). ¿Correcto?¿Debería usar AppDomain.UnhandledException y Application.DispatcherUnhandledException?

Además, ¿debería dar al usuario la posibilidad de continuar el programa para ambos, o solo la Application.DispatcherUnhandledException?

ejemplo de código siguiente se encarga tanto AppDomain.UnhandledException y Application.DispatcherUnhandledException, y tanto le da al usuario la opción de tratar de continuar a pesar de la excepción.

[gracias y una parte del código a continuación se levanta de otras respuestas]

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 [redactado]

/// <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; 
} 

Respuesta

31
  • AppDomain.CurrentDomain.UnhandledException en teoría captura todas las excepciones en todas las discusiones del dominio de aplicación. En lugar de e.Handled, use e.SetObserved() si no desea que el proceso finalice. Sin embargo, encontré que esto es muy poco confiable.

  • Application.Current.DispatcherUnhandledException capta todas las excepciones en el hilo de la interfaz de usuario. Esto parece funcionar de manera confiable, y reemplazará el controlador AppDomain.CurrentDomain.UnhandledException en el subproceso de la interfaz de usuario (tiene prioridad). Use e.Handled = true para mantener la aplicación en ejecución.

  • Para capturar excepciones en otros hilos (en el mejor de los casos, se manejan en su propio hilo), encontré que System.Threading.Tasks.Task (solo .NET 4.0 y superior) es de bajo mantenimiento. Manejar excepciones en tareas con el método .ContinueWith(...,TaskContinuationOptions.OnlyOnFaulted). Ver mi respuesta here para más detalles.

+4

No hay 'e.SetObserved()' al manejar 'AppDomain.CurrentDomain.UnhandledException'. Está confundiendo esto con los argumentos de 'TaskScheduler.UnobservedTaskException'. – Hannobo

4

Un AppDomain.UnhandledException manejador está cableado como:

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; 

Pero no pude encontrar una forma de marcar como manejada en el controlador, por lo que siempre parece dar como resultado el cierre de una aplicación sin importar lo que haga. Entonces no creo que esto sea de mucha utilidad.

Es mejor manejar Application.Current.DispatcherUnhandledException y probar CommunicationObjectFaultedException - ya que puede recuperarse de eso simplemente reinicializando su proxy, exactamente como lo hizo en la conexión inicial. Por ejemplo:

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(); 
} 

donde Initialize tiene su código de instanciación/conexión proxy inicial.

En el código que publicó anteriormente, sospecho que está manipulando DispatcherUnhandledExceptiondos veces - cableando un controlador en xaml AND en el código.

+0

Tampoco estoy seguro de si existe la posibilidad de que ambos entreguen una excepción para el hilo de UI, pero sospecho que puede suceder. Es por eso que ambos han sido e.handled = verdadero al final, eso debería evitar que se maneje dos veces. – Kyle

Cuestiones relacionadas