Обработка проблемы необработанных исключений

Я хотел установить некоторый обработчик для всех неожиданных исключений, которые я, возможно, не уловил в своем коде. В Program.Main() Я использовал следующий код:

AppDomain.CurrentDomain.UnhandledException
    += new UnhandledExceptionEventHandler(ErrorHandler.HandleException);

Но это не сработало, как я ожидал. Когда я запустил приложение в режиме отладки и выдал исключение, оно вызвало обработчик, но после этого в Visual Studio появился помощник исключения, как будто исключение произошло без какой-либо обработки. Я попробовал Application.Exit() внутри обработчика, но он не сработал.

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

6 ответов

Решение

Это потому, что вы запускаете его через Visual Studio в режиме отладки. Если вы выпустите и установите свое приложение где-то еще, ничего, кроме вашего глобального обработчика исключений, не будет обработано.

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

using System;

static class Program
{
  [STAThread]
  static void Main(string[] argv)
  {
    try
    {
      AppDomain.CurrentDomain.UnhandledException += (sender,e)
      => FatalExceptionObject(e.ExceptionObject);

      Application.ThreadException += (sender,e)
      => FatalExceptionHandler.Handle(e.Exception);

      // whatever you need/want here

      Application.Run(new MainWindow());
    }
    catch (Exception huh)
    {
      FatalExceptionHandler.Handle(huh);
    }
  }

  static void FatalExceptionObject(object exceptionObject) {
    var huh = exceptionObject as Exception;
    if (huh == null) {
      huh = new NotSupportedException(
        "Unhandled exception doesn't derive from System.Exception: "
         + exceptionObject.ToString()
      );
    }
    FatalExceptionHandler.Handle(huh);
  }
}

Может быть, это то, что вы считаете полезным? Этот основной код маршрутизирует все три способа перехвата неожиданных исключений верхнего уровня с помощью одного вызова метода. Все, что вам сейчас нужно, это статический класс FatalExceptionHandler что включает в себя обработку исключений высшего уровня в его Handle метод.

И действительно, любой разработчик приложений знает, что на самом деле есть только две вещи:

  1. Показать / записать исключение, как вы считаете нужным
  2. Убедитесь, что вы выходите / убиваете процесс приложения

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

Мертвые программы не лгут...;-)

Обратите внимание, что необработанные исключения все еще довольно фатальны; Вы можете действительно использовать это только для регистрации, или, может быть, какой-нибудь поспешный крупный план. Ни это ни Application.ThreadException может использоваться как глобальный приемник ошибок.

Лучше всего добавить правильную обработку - например, вокруг всего вашего Main() логика. Обратите внимание, что даже это не может поймать несколько исключений, таких как ошибки во время загрузки формы (которые становятся особенно неприятными - вы можете поймать их с подключенным отладчиком, но не без).

Возможно, вы ищете Environment.Exit(int errorcode)

Такое поведение разработано.

Но есть обходной путь.

Либо ты звонишь Process.GetCurrentProcess().Kill(); в обработчике, или просто не позволяйте обработчику заканчиваться.

Проверьте пример:

class Program
{
    void Run()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

        Console.WriteLine("Press enter to exit.");

        do
        {
            (new Thread(delegate()
            {
                throw new ArgumentException("ha-ha");
            })).Start();

        } while (Console.ReadLine().Trim().ToLowerInvariant() == "x");


        Console.WriteLine("last good-bye");
    }

    int r = 0;

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Interlocked.Increment(ref r);
        Console.WriteLine("handled. {0}", r);
        Console.WriteLine("Terminating " + e.IsTerminating.ToString());

        Thread.CurrentThread.IsBackground = true;
        Thread.CurrentThread.Name = "Dead thread";            

        while (true)
            Thread.Sleep(TimeSpan.FromHours(1));
        //Process.GetCurrentProcess().Kill();
    }

    static void Main(string[] args)
    {
        Console.WriteLine("...");
        (new Program()).Run();
    }
}

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

Но это должно быть сделано, чтобы изящно сообщать об исключениях.

Примечание: если ваше приложение является приложением Windows Forms, не забудьте:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

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

Другие вопросы по тегам