2009-09-24 13 views
272

A volte, in circostanze non riproducibili, l'applicazione WPF si arresta in modo anomalo senza alcun messaggio. L'applicazione si chiude semplicemente all'istante.Gestore di eccezioni globali WPF

Dove è il posto migliore per implementare il blocco Try/Catch globale. Almeno ho implementare un messagebox con: "Ci scusiamo per l'inconveniente ..."

+10

Gotta love come i collegamenti duplicati di nuovo a questa domanda – Jay

+0

Questa domanda ha risposte migliori. –

risposta

138

È possibile gestire l'evento AppDomain.UnhandledException

EDIT: In realtà, questo evento è probabilmente più adeguata: Application.DispatcherUnhandledException

+8

Aggiungi il gestore nel costruttore di moduli come questo: AppDomain.Current.UnhandledException + = ... – Dabblernl

+11

Scarsa idea se crei più istanze della finestra ... –

+1

Ciao Thomas, grazie per la tua risposta. Appdomain.UnHandledException funziona alla grande per me. –

428

È può intercettare le eccezioni non gestite a diversi livelli:

  1. AppDomain.CurrentDomain.UnhandledException Da tutte le discussioni in AppDomain.
  2. Dispatcher.UnhandledException Da una singola stringa del dispatcher dell'interfaccia utente specifica.
  3. Application.Current.DispatcherUnhandledException Dal principale thread di dispacciamento UI nell'applicazione WPF.
  4. TaskScheduler.UnobservedTaskException dall'interno di ogni AppDomain che utilizza uno schedulatore di attività per operazioni asincrone.

È necessario considerare il livello necessario per intercettare le eccezioni non gestite su.

La decisione tra 2 e 3 dipende dall'utilizzo di più thread WPF. Questa è una situazione piuttosto esotica e se non sei sicuro che lo sei o meno, allora è molto probabile che tu non lo sia.

+12

Nota per il tuo articolo n. 3, ho dovuto inserire. Corrente seguente Applicazione come questa: Application.Current.DispatcherUnhandledException + = ... –

+1

@Keith G - tutto questi eventi sono membri di istanza, quindi è necessario collegarli per ogni oggetto richiesto, a seconda delle circostanze. –

+0

Se l'applicazione si chiude senza accedere a nessuno di questi gestori di eccezioni, è possibile che il sistema sia stato arrestato con Environment.FailFast – Mo0gles

89

un rapido esempio di codice per 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; 
    } 

ho aggiunto questo codice in App.xaml.cs

+0

+1 per il codice taglia/incolla. Se stai cercando di ravvivare la finestra di dialogo dei messaggi di errore, il toolkit esteso WPF ha un [messagebox control] (http://wpftoolkit.codeplex.com/wikipage?title=Extended%20WPF%20Toolkit%20Controls). –

+12

Si noti che in alcune situazioni, l'impostazione 'e.Handled = true' può causare la chiusura dell'interfaccia utente dell'applicazione, mentre il processo rimane in esecuzione sulla macchina in modo silenzioso. – qJake

39

Io uso il seguente codice nel mio applicazioni WPF per mostrare una " Scusa per l'inconveniente "finestra di dialogo ogni volta che si verifica un'eccezione non gestita. Mostra il messaggio di eccezione e chiede all'utente se desidera chiudere l'app o ignorare l'eccezione e continuare (quest'ultimo caso è utile quando si verificano eccezioni non fatali e l'utente può continuare a utilizzare l'app).

Nel App.xaml aggiungere il gestore di eventi di avvio:

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

Nel codice App.xaml.cs aggiungere la funzione del gestore di eventi di avvio che registrerà il gestore di eventi di applicazione globale:

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 che il collegamento è morto – McKay

+2

@McKay è stato eliminato come un duplicato di questo: http://stackoverflow.com/questions/2004629/what-is-the-best-way-in-c-sharp-to-determine- se-the-programmatore-è-running-t – weston

+0

@Weston Grazie per l'aggiornamento. – McKay

10

In Oltre ai post sopra:

Application.Current.DispatcherUnhandledException 

non rileverà eccezioni che vengono lanciate da un altro er thread quindi il thread principale. Devi gestire queste eccezioni sulla sua discussione effettiva.Ma se si desidera gestire sul vostro gestore eccezioni globale si può passare al thread principale:

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

Come accennato in precedenza

Application.Current.DispatcherUnhandledException non prenderà eccezioni generate da un altro thread quindi il thread principale.

Tale effettivo dipenderà da come il filo è stato creato

Un caso che non viene gestito dal Application.Current.DispatcherUnhandledException è System.Windows.Forms.Timer che Application.ThreadException può essere utilizzato per gestire questi se si esegue moduli su altri thread rispetto al filo principale sarà necessario impostare Application.ThreadException da ogni tale filo

+0

la copia da un altro thread in cui la risposta da Hertzel Guinness: nel app.config impedirà il tuo eccezione discussioni secondaria dalla chiusura in basso l'applicazione " –

3

Una soluzione completa è here

è spiegato molto bello con codice di esempio. Tuttavia, fai attenzione a non chiudere l'applicazione. Aggiungi la riga Application.Current.Shutdown(); per chiudere con garbo l'app.

7

La risposta migliore è probabilmente https://stackoverflow.com/a/1472562/601990.

Ecco alcuni codice che mostra come 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

Potrebbe essere necessario includere' #if DEBUG' in modo che Visual Studio gestisca eccezioni come normale solo al debugging. Soluzione impressionante –

+1

invece di '#if DEBUG' dovresti usare il' [Conditional ("DEBUG") ] Attributo su "RegisterGlobalExceptionHandling", in questo modo è possibile assicurarsi che il codice venga compilato quando si modifica il target del compilatore. – MovGP0

+0

Inoltre, è preferibile mantenere la registrazione di glo bal eccezioni anche nel codice di produzione. è possibile utilizzare il 'ConditionalAttribute' per la configurazione del logger all'interno della propria installazione di dipendenze e modificare semplicemente la verbosità di registrazione. – MovGP0

Problemi correlati