it-swarm.com.ru

Глобальный обработчик исключений WPF

Иногда при невоспроизводимых обстоятельствах мое приложение WPF вылетает без какого-либо сообщения. Приложение просто закрывается мгновенно.

Где лучшее место для реализации глобального блока Try/Catch. По крайней мере, я должен реализовать сообщение с: "Извините за неудобства ..."

308
Scott Olson

Вы можете обработать событие AppDomain.UnhandledException

Правка: на самом деле, это событие, вероятно, более адекватно: Application.DispatcherUnhandledException

155
Thomas Levesque

Вы можете перехватывать необработанные исключения на разных уровнях:

  1. AppDomain.CurrentDomain.UnhandledException Из всех потоков в AppDomain.
  2. Dispatcher.UnhandledException Из одного конкретного потока диспетчера пользовательского интерфейса.
  3. Application.Current.DispatcherUnhandledException Из потока основного диспетчера пользовательского интерфейса в вашем приложении WPF.
  4. TaskScheduler.UnobservedTaskException внутри каждого AppDomain, который использует планировщик задач для асинхронных операций.

Вы должны рассмотреть, на каком уровне вам нужно перехватывать необработанные исключения.

Выбор между № 2 и № 3 зависит от того, используете ли вы более одного потока WPF. Это довольно экзотическая ситуация, и если вы не уверены, так это или нет, то, скорее всего, нет.

479
Drew Noakes

Быстрый пример кода для 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;
}

Я добавил этот код в App.xaml.cs

106
Sergey

Я использую следующий код в своих приложениях WPF для отображения диалогового окна "Приносим извинения за неудобства" при возникновении необработанного исключения. Он показывает сообщение об исключении и спрашивает пользователя, хотят ли они закрыть приложение или проигнорировать исключение и продолжить (последний случай удобен, когда возникают нефатальные исключения и пользователь все еще может продолжать использовать приложение).

В App.xaml добавьте обработчик события Startup:

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

В коде App.xaml.cs добавьте функцию-обработчик события запуска, которая будет регистрировать глобальный обработчик события приложения:

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

Лучший ответ, вероятно, https://stackoverflow.com/a/1472562/60199 .

Вот некоторый код, который показывает, как его использовать:

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

В дополнение к постам выше:

Application.Current.DispatcherUnhandledException

не будет перехватывать исключения, которые вызываются из другого потока, а не из основного потока. Вы должны обработать эти исключения в его реальном потоке. Но если вы хотите обработать их в своем глобальном обработчике исключений, вы можете передать его в основной поток:

 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

В дополнение к ответу Томаса класс Application также имеет событие DispatcherUnhandledException , которое вы можете обработать.

3
dustyburwell

Полное решение здесь

это очень хорошо объясняется с примером кода. Однако будьте осторожны, чтобы приложение не закрывалось. Добавьте строку Application.Current.Shutdown (); изящно закрыть приложение.

3
karpanai

Как уже упоминалось выше

Application.Current.DispatcherUnhandledException не будет перехватывать исключения, которые вызываются из другого потока, а не из основного потока.

Это зависит от того, как был создан поток

Одним из случаев, который не обрабатывается Application.Current.DispatcherUnhandledException, является System.Windows.Forms.Timer, для которого Application.ThreadException может использоваться для их обработки, если вы запускаете Forms в других потоках, отличных от основного потока, вам необходимо установить Application.ThreadException из каждой такой нити

1
Jens