2009-09-24 17 views
272

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

Donde es el mejor lugar para implementar el bloque Try/Catch global. Por lo menos tengo para implementar un cuadro de mensaje con: "Lo siento por las molestias ..."

+10

por que el amor cómo los enlaces duplicados de nuevo a esta pregunta – Jay

+0

Esta pregunta tiene mejores respuestas. –

Respuesta

138

Usted puede controlar el evento AppDomain.UnhandledException

EDIT: En realidad, este evento es probablemente más adecuada: Application.DispatcherUnhandledException

+8

Agregue el controlador en el constructor de formularios de esta manera: AppDomain.Current.UnhandledException + = ... – Dabblernl

+11

Mala idea si crea varias instancias de la ventana ... –

+1

Hola Thomas, gracias por su respuesta. Appdomain.UnHandledException funciona muy bien para mí. –

428

Usted puede excepciones no controladas trampa a diferentes niveles:

  1. AppDomain.CurrentDomain.UnhandledException de todas las discusiones en el dominio de aplicación.
  2. Dispatcher.UnhandledException Desde un solo subproceso de despachador de UI específico.
  3. Application.Current.DispatcherUnhandledException Desde el principal UI despachador de hilo 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á usando más de un hilo 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.

+12

Nota para su artículo # 3, tuve que poner. Aplicación actual siguiente siguiente: Application.Current.DispatcherUnhandledException + = ... –

+1

@Keith G - todos de estos eventos son miembros de instancias, por lo que necesitaría engancharlos para cada objeto requerido, dependiendo de sus circunstancias. –

+0

Si la aplicación simplemente se cierra sin ir a ninguno de estos manejadores de excepciones, también puede ser que el Sistema se haya apagado con Environment.FailFast – Mo0gles

89

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

He añadido este código en App.xaml.cs

+0

+1 para código de cortar/pegar. Si está buscando darle más vida al cuadro de diálogo de mensaje de error, WPF extended toolkit tiene un [control de cuadro de mensaje] (http://wpftoolkit.codeplex.com/wikipage?title=Extended%20WPF%20Toolkit%20Controls). –

+12

Tenga en cuenta que en determinadas situaciones, ajuste 'e.Handled = true' puede hacer que se cierre la IU de la aplicación, mientras el proceso se ejecuta en la máquina silenciosamente. – qJake

39

utilizo el siguiente código en mis aplicaciones WPF para mostrar un " Disculpe las molestias "cuadro de diálogo cada vez que se produce 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 agregar el controlador de eventos de inicio:

<Application .... Startup="Application_Startup"> 

En el código App.xaml.cs Agregue la función de controlador de eventos de arranque que registrará el controlador de eventos 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(); 
    } 
} 
+0

@Weston ese enlace está muerto – McKay

+2

@McKay se borró como una estafa de esto: http://stackoverflow.com/questions/2004629/what-is-the-best-way-in-c-sharp-to-determine- si-el-programador-está-corriendo-t – weston

+0

@Weston Gracias por actualizar. – McKay

10

En Además de los puestos de arriba:

Application.Current.DispatcherUnhandledException 

no va a capturar las excepciones que se lanzan desde anoth hilo de rosca luego el hilo principal. Tienes que manejar esas excepciones en su Rosca real.Pero si quieres para manejarlos en su gestor de excepciones mundial puede pasarlo 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<Exception>((exc) => 
        { 
         throw new Exception("Exception from another Thread", exc); 
        }), ex); 
     } 
    }); 
1

Como se mencionó anteriormente

Application.Current.DispatcherUnhandledException no cogerá excepciones lanzadas de otro hilo, luego el hilo principal.

Eso reales dependen de cómo se creó el hilo

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

+0

copiar de otro hilo en lo que la respuesta por Hertzel Guinness: en el app.config evitará que su hilos excepción secundaria de cierre abajo de la aplicación " –

3

Una solución completa es here

se explica muy agradable 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.

7

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<ILogger>(); 
     RegisterGlobalExceptionHandling(logger); 

     // Bootstrapping Dependency Injection 
     // injects ViewModel into MainWindow.xaml 
     // remember to remove the StartupUri attribute in App.xaml 
     var mainWindow = resolver.Resolve<Pages.MainWindow>(); 
     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); 
    } 
} 
+0

Podría ser necesario incluir' #de DEPURACIÓN 'para que Visual Studio maneje excepciones como normal solo cuando se depure. Solución impresionante. –

+1

en lugar de '#si DEPURAR', debe usar' [Condicional ("DEPURAR") ] 'atributo en 'RegisterGlobalExceptionHandling' en su lugar. De esta manera puede asegurarse de que el código se compila al cambiar el objetivo del compilador. – MovGP0

+0

además, es preferible mantener el registro de glo bal excepciones también en el código de producción. puede usar 'ConditionalAttribute' para la configuración del registrador dentro de su configuración de inyección de dependencia y simplemente cambiar la verbosidad de registro. – MovGP0

Cuestiones relacionadas