it-swarm.com.de

Globale WPF-Ausnahmebehandlung

Manchmal stürzt meine WPF-Anwendung unter nicht reproduzierbaren Umständen ohne Meldung ab. Die Anwendung wird einfach sofort geschlossen.

Wo ist der beste Ort, um den globalen Try/Catch-Block zu implementieren. Zumindest muss ich eine Messagebox implementieren mit: "Sorry für die Unannehmlichkeiten ..."

315
Scott Olson

Sie können mit AppDomain.UnhandledException Ereignis

BEARBEITEN: Eigentlich ist diese Veranstaltung wahrscheinlich angemessener: Application.DispatcherUnhandledException

159
Thomas Levesque

Sie können nicht behandelte Ausnahmen auf verschiedenen Ebenen abfangen:

  1. AppDomain.CurrentDomain.UnhandledException Aus allen Threads in der AppDomain.
  2. Dispatcher.UnhandledException Von einem einzelnen bestimmten UI-Dispatcher-Thread.
  3. Application.Current.DispatcherUnhandledException Aus dem Haupt-UI-Dispatcher-Thread in Ihrer WPF-Anwendung.
  4. TaskScheduler.UnobservedTaskException aus jeder AppDomain, die einen Taskplaner für asynchrone Vorgänge verwendet.

Sie sollten überlegen, auf welcher Ebene Sie nicht behandelte Ausnahmen abfangen müssen.

Die Entscheidung zwischen 2 und 3 hängt davon ab, ob Sie mehr als einen WPF-Thread verwenden. Dies ist eine ziemlich exotische Situation, und wenn Sie sich nicht sicher sind, ob Sie es sind oder nicht, ist es sehr wahrscheinlich, dass Sie es nicht sind.

485
Drew Noakes

Ein kurzes Codebeispiel für Application.Dispatcher.UnhandledException:

public App() {
    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);
    // OR whatever you want like logging etc. MessageBox it's just example
    // for quick debugging etc.
    e.Handled = true;
}

Ich habe diesen Code in App.xaml.cs hinzugefügt

107
Sergey

Ich verwende den folgenden Code in meinen WPF-Apps, um ein Dialogfeld "Entschuldigung für die Unannehmlichkeiten" anzuzeigen, wenn eine nicht behandelte Ausnahme auftritt. Es zeigt die Ausnahmemeldung an und fragt den Benutzer, ob er die App schließen oder die Ausnahme ignorieren und fortfahren möchte (letzterer Fall ist praktisch, wenn nicht schwerwiegende Ausnahmen auftreten und der Benutzer die App normalerweise weiterhin verwenden kann).

Fügen Sie in App.xaml den Startup-Ereignishandler hinzu:

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

Fügen Sie in App.xaml.cs-Code die Startup-Ereignishandlerfunktion hinzu, mit der der globale Anwendungsereignishandler registriert wird:

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

Die beste Antwort ist wahrscheinlich https://stackoverflow.com/a/1472562/60199 .

Hier ist ein Code, der zeigt, wie man es benutzt:

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);
    }
}
15
MovGP0

Zusätzlich zu den obigen Beiträgen:

Application.Current.DispatcherUnhandledException

fängt keine Ausnahmen ab, die von einem anderen Thread als dem Haupt-Thread ausgelöst werden. Sie müssen diese Ausnahmen in ihrem eigentlichen Thread behandeln. Wenn Sie sie jedoch in Ihrem globalen Ausnahmehandler verarbeiten möchten, können Sie sie an den Haupt-Thread übergeben:

 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);
        }
    });
11
Tobias Hoefer

Ergänzend zu Thomas 'Antwort enthält die Klasse Application auch das Ereignis DispatcherUnhandledException , das Sie verarbeiten können.

3
dustyburwell

Eine vollständige Lösung ist hier

es ist sehr schön mit Beispielcode erklärt. Achten Sie jedoch darauf, dass die Anwendung nicht geschlossen wird. Fügen Sie die Zeile Application.Current.Shutdown () hinzu. um die App anmutig zu schließen.

3
karpanai

Wie oben erwähnt

Application.Current.DispatcherUnhandledException fängt keine Ausnahmen ab, die von einem anderen Thread als dem Hauptthread ausgelöst werden.

Das hängt davon ab, wie der Thread erstellt wurde

Ein Fall, der von Application.Current.DispatcherUnhandledException nicht behandelt wird, ist System.Windows.Forms.Timer, für den Application.ThreadException verwendet werden kann, um diese zu behandeln, wenn Sie Forms in anderen Threads als dem Hauptthread ausführen, den Sie zum Festlegen von Application.ThreadException benötigen von jedem solchen Thread

2
Jens