Глобальные исключения исключений в приложении WPF?

У нас есть приложение WPF, где его части могут вызывать исключения во время выполнения. Я хотел бы глобально поймать любое необработанное исключение и зарегистрировать их, но в противном случае продолжить выполнение программы, как будто ничего не произошло (вроде VB On Error Resume Next).

Возможно ли это в С#? И если да, то где именно мне нужно будет поместить код обработки исключений?

В настоящее время я не вижу ни одной точки, где я мог бы обернуть вокруг try/catch вокруг, и который поймал бы все исключения, которые могут произойти. И даже тогда я бы оставил все, что было выполнено из-за улова. Или я думаю в ужасно неправильных направлениях здесь?

ETA:. Потому что многие люди ниже указали: приложение не для управления атомными электростанциями. Если он выйдет из строя, это не так много, но случайные исключения, которые в основном связаны с UI, являются неприятностью в контексте, где он будет использоваться. Были (и, вероятно, все еще) некоторые из них, и поскольку он использует архитектуру плагина и может быть расширен другими (также студенты в этом случае, поэтому нет опытных разработчиков, которые могут писать полностью безошибочный код).

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

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

Для немедленного использования приложения не так много вещей, которые могут произойти при исключении:

  • Обработка исключений - диалог ошибок и выход приложения. Эксперимент должен быть повторен, хотя, вероятно, с другим предметом. Ошибок не было зарегистрировано, что является неудачным.
  • Общая обработка исключений - невосприимчивость к ошибкам, отсутствие вреда. Это должен быть общий случай, который можно судить по всем ошибкам, которые мы наблюдали во время разработки. Игнорирование подобных ошибок не должно иметь немедленных последствий; основные структуры данных проверены достаточно хорошо, чтобы они могли легко выжить.
  • Общая обработка исключений - серьезная ошибка в ловушке, возможно, сбой в более поздней точке. Это может произойти редко. Мы этого никогда не видели. Ошибка регистрируется в любом случае, и авария может быть неизбежной. Так что это концептуально похоже на самый первый случай. За исключением того, что у нас есть трассировка стека. И в большинстве случаев пользователь даже не заметит.

Что касается данных эксперимента, созданных программой: серьезная ошибка в худшем случае просто не приведет к тому, что данные не будут записаны. Тонкие изменения, которые изменяют результат эксперимента настолько незначительно, маловероятны. И даже в этом случае, если результаты кажутся сомнительными, была зарегистрирована ошибка; вы можете выбросить эту точку данных, если это общий выброс.

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

Боковое примечание. Для этого приложения есть ровно один пользователь. Это не что-то похожее на Windows или Office, которые используются миллионами, где стоимость получения исключений для пользователя вообще будет совсем другим.

Ответ 1

Используйте Application.DispatcherUnhandledException Event. См. этот вопрос для сводки (см. ответ Drew Noakes).

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

Ответ 2

Пример кода с использованием NLog, который будет перехватывать исключения, генерируемые всеми потоками в домене приложений, из потока диспетчера пользовательского интерфейса и из асинхронных функций:

App.xaml.cs:

public partial class App : Application
{
    private static Logger _logger = LogManager.GetCurrentClassLogger();

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        SetupExceptionHandling();
    }

    private void SetupExceptionHandling()
    {
        AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            LogUnhandledException((Exception)e.ExceptionObject, "AppDomain.CurrentDomain.UnhandledException");

        Dispatcher.UnhandledException += (s, e) =>
            LogUnhandledException(e.Exception, "Application.Current.DispatcherUnhandledException");

        TaskScheduler.UnobservedTaskException += (s, e) =>
            LogUnhandledException(e.Exception, "TaskScheduler.UnobservedTaskException");
    }

    private void LogUnhandledException(Exception exception, string source)
    {
        string message = $"Unhandled exception ({source})";
        try
        {
            System.Reflection.AssemblyName assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName();
            message = string.Format("Unhandled exception in {0} v{1}", assemblyName.Name, assemblyName.Version);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Exception in LogUnhandledException");
        }
        finally
        {
            _logger.Error(exception, message);
        }
    }

Ответ 3

AppDomain.UnhandledException Событие

Это событие предоставляет уведомление о неперехваченных исключениях. Это позволяет приложение для регистрации информации об исключении перед системой обработчик по умолчанию сообщает об исключении пользователю и завершает приложение.

   public App()
   {
      AppDomain currentDomain = AppDomain.CurrentDomain;
      currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);    
   }

   static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
   {
      Exception e = (Exception) args.ExceptionObject;
      Console.WriteLine("MyHandler caught : " + e.Message);
      Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
   }

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

Например, предположим, что поток начинается в домене приложения "AD1", вызывает метод в домене приложения "AD2", а оттуда вызывает метод в домене приложения "AD3", где он генерирует исключение. первый домен приложения, в котором событие UnhandledException может быть поднятый "AD1". Если этот домен приложения не является стандартным домена приложения, событие также может быть поднято по умолчанию домена приложения.

Ответ 4

В дополнение к тому, что упоминалось здесь другими, обратите внимание, что объединение Application.DispatcherUnhandledException (и его похожих) с

<configuration>
  <runtime>  
    <legacyUnhandledExceptionPolicy enabled="1" />
  </runtime>
</configuration>

в app.config предотвратит исключение приложения вторичными потоками для закрытия приложения.

Ответ 5

Вот полный пример, используя NLog

using NLog;
using System;
using System.Windows;

namespace MyApp
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public App()
        {
            var currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }

        private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            var ex = (Exception)e.ExceptionObject;
            logger.Error("UnhandledException caught : " + ex.Message);
            logger.Error("UnhandledException StackTrace : " + ex.StackTrace);
            logger.Fatal("Runtime terminating: {0}", e.IsTerminating);
        }        
    }


}

Ответ 6

Как "VB On Error Resume Next?" Это звучит страшно. Первая рекомендация не делает этого. Вторая рекомендация - не делать этого и не думать об этом. Вам нужно лучше изолировать свои недостатки. Что касается подхода к этой проблеме, это зависит от того, как структурирован код. Если вы используете шаблон, подобный MVC или тому подобное, тогда это не должно быть слишком сложно и, безусловно, не требует глобального исключения swallower. Во-вторых, найдите хорошую библиотеку регистрации, такую ​​как log4net или используйте трассировку. Нам нужно узнать более подробную информацию о том, какие исключения вы говорите и какие части вашего приложения могут привести к выбросам исключений.