Manejador de excepciones globales de WPF

A veces, en circunstancias no reproducibles, mi aplicación WPF falla sin ningún mensaje. La aplicación simplemente se cierra al instante.

¿Dónde está el mejor lugar para implementar el bloque Try / Catch global? Al menos tengo que implementar un cuadro de mensaje con: “Disculpa las molestias …”

Puede manejar el evento AppDomain.UnhandledException

EDITAR: en realidad, este evento es probablemente más adecuado: Application.DispatcherUnhandledException

Puede atrapar excepciones no controladas en diferentes niveles:

  1. AppDomain.CurrentDomain.UnhandledException De todos los hilos en el AppDomain.
  2. Dispatcher.UnhandledException un único hilo de distribuidor de IU específico.
  3. Application.Current.DispatcherUnhandledException Desde la hebra del distribuidor de UI principal en su aplicación WPF.
  4. TaskScheduler.UnobservedTaskException desde cada AppDomain que utiliza un planificador de tareas para operaciones asincrónicas.

Debería considerar qué nivel necesita para atrapar las excepciones no controladas en.

Decidir entre # 2 y # 3 depende de si estás usando más de un hilo de WPF. Esta es una situación bastante exótica y, si no está seguro de si lo es o no, lo más probable es que no lo sea.

Un ejemplo rápido de código para Application.Dispatcher.UnhandledException:

  public App() :base() { this.Dispatcher.UnhandledException += OnDispatcherUnhandledException; } void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) { string errorMessage = string.Format("An unhandled exception occurred: {0}", e.Exception.Message); MessageBox.Show(errorMessage, "Error", MessageBoxButton.OK, MessageBoxImage.Error); e.Handled = true; } 

Agregué este código en App.xaml.cs

Utilizo el siguiente código en mis aplicaciones WPF para mostrar un cuadro de diálogo “Disculpa la inconveniencia” cada vez que ocurre una excepción no controlada. Muestra el mensaje de excepción y le pregunta al usuario si quiere cerrar la aplicación o ignorar la excepción y continuar (este último caso es conveniente cuando se producen excepciones no fatales y el usuario puede seguir utilizando la aplicación).

En App.xaml agrega el controlador de eventos de inicio:

  

En el código App.xaml.cs, agregue la función del controlador de eventos de inicio que registrará el controlador de eventos de la aplicación global:

 using System.Windows.Threading; private void Application_Startup(object sender, StartupEventArgs e) { // Global exception handling Application.Current.DispatcherUnhandledException += new DispatcherUnhandledExceptionEventHandler(AppDispatcherUnhandledException); } void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) { \#if DEBUG // In debug mode do not custom-handle the exception, let Visual Studio handle it e.Handled = false; \#else ShowUnhandledException(e); \#endif } void ShowUnhandledException(DispatcherUnhandledExceptionEventArgs e) { e.Handled = true; string errorMessage = string.Format("An application error occurred.\nPlease check whether your data is correct and repeat the action. If this error occurs again there seems to be a more serious malfunction 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 + (e.Exception.InnerException != null ? "\n" + e.Exception.InnerException.Message : null)); if (MessageBox.Show(errorMessage, "Application 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(); } } 

La mejor respuesta es probablemente https://stackoverflow.com/a/1472562/601990 .

Aquí hay un código que muestra cómo usarlo:

App.xaml.cs

 public sealed partial class App { protected override void OnStartup(StartupEventArgs e) { // setting up the Dependency Injection container var resolver = ResolverFactory.Get(); // getting the ILogger or ILog interface var logger = resolver.Resolve(); RegisterGlobalExceptionHandling(logger); // Bootstrapping Dependency Injection // injects ViewModel into MainWindow.xaml // remember to remove the StartupUri attribute in App.xaml var mainWindow = resolver.Resolve(); mainWindow.Show(); } private void RegisterGlobalExceptionHandling(ILogger log) { // this is the line you really want AppDomain.CurrentDomain.UnhandledException += (sender, args) => CurrentDomainOnUnhandledException(args, log); // optional: hooking up some more handlers // remember that you need to hook up additional handlers when // logging from other dispatchers, shedulers, or applications Application.Dispatcher.UnhandledException += (sender, args) => DispatcherOnUnhandledException(args, log); Application.Current.DispatcherUnhandledException += (sender, args) => CurrentOnDispatcherUnhandledException(args, log); TaskScheduler.UnobservedTaskException += (sender, args) => TaskSchedulerOnUnobservedTaskException(args, log); } private static void TaskSchedulerOnUnobservedTaskException(UnobservedTaskExceptionEventArgs args, ILogger log) { log.Error(args.Exception, args.Exception.Message); args.SetObserved(); } private static void CurrentOnDispatcherUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log) { log.Error(args.Exception, args.Exception.Message); // args.Handled = true; } private static void DispatcherOnUnhandledException(DispatcherUnhandledExceptionEventArgs args, ILogger log) { log.Error(args.Exception, args.Exception.Message); // args.Handled = true; } private static void CurrentDomainOnUnhandledException(UnhandledExceptionEventArgs args, ILogger log) { var exception = args.ExceptionObject as Exception; var terminatingMessage = args.IsTerminating ? " The application is terminating." : string.Empty; var exceptionMessage = exception?.Message ?? "An unmanaged exception occured."; var message = string.Concat(exceptionMessage, terminatingMessage); log.Error(exception, message); } } 

Además de las publicaciones anteriores:

 Application.Current.DispatcherUnhandledException 

no detectará las excepciones que se lanzan desde otro subproceso luego el subproceso principal. Tienes que manejar esas excepciones en su Rosca real. Pero si quiere manejarlos en su manejador global de excepciones, puede pasarlos al hilo principal:

  System.Threading.Thread t = new System.Threading.Thread(() => { try { ... //this exception will not be catched by //Application.DispatcherUnhandledException throw new Exception("huh.."); ... } catch (Exception ex) { //But we can handle it in the throwing thread //and pass it to the main thread wehre Application. //DispatcherUnhandledException can handle it System.Windows.Application.Current.Dispatcher.Invoke( System.Windows.Threading.DispatcherPriority.Normal, new Action((exc) => { throw new Exception("Exception from another Thread", exc); }), ex); } }); 

Para complementar la respuesta de Thomas, la clase Application también tiene el evento DispatcherUnhandledException que puede manejar.

Una solución completa está aquí

se explica muy bien con código de muestra. Sin embargo, tenga cuidado de que no cierre la aplicación. Agregue la línea Application.Current.Shutdown (); para cerrar con gracia la aplicación.

Como se ha mencionado más arriba

Application.Current.DispatcherUnhandledException no detectará las excepciones que se lanzan desde otro subproceso luego el subproceso principal.

Eso real depende de cómo se creó el hilo

Un caso que no es manejado por Application.Current.DispatcherUnhandledException es System.Windows.Forms.Timer para el cual Application.ThreadException se puede usar para manejar estos si ejecuta Formularios en otros hilos que el hilo principal que necesitará para establecer Application.ThreadException de cada hilo