Глобальный обработчик исключений .NET в консольном приложении - PullRequest
183 голосов
/ 28 июня 2010

Вопрос. Я хочу определить глобальный обработчик исключений для необработанных исключений в моем консольном приложении. В asp.net его можно определить в global.asax, а в приложениях / службах windows - как ниже

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

Но как определить глобальный обработчик исключений для консольного приложения?
Кажется, текущий домен не работает (.NET 2.0)?

Редактировать:

Argh, глупая ошибка.
В VB.NET необходимо добавить ключевое слово «AddHandler» перед currentDomain, иначе не будет видно событие UnhandledException в IntelliSense ...
Это потому, что компиляторы VB.NET и C # по-разному обрабатывают обработку событий.

Ответы [ 4 ]

261 голосов
/ 28 июня 2010

Нет, это правильный способ сделать это. Это сработало именно так, как и должно быть, что-то, из чего вы можете работать, возможно:

using System;

class Program {
    static void Main(string[] args) {
        System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}

Имейте в виду, что таким способом вы не можете отловить исключения загрузки типов и файлов, генерируемые джиттером. Они происходят до того, как ваш метод Main () начнет работать. Чтобы отловить их, нужно отложить дрожание, переместить рискованный код в другой метод и применить к нему атрибут [MethodImpl (MethodImplOptions.NoInlining)].

23 голосов
/ 29 июня 2010

Если у вас однопоточное приложение, вы можете использовать простой try / catch в функции Main, однако это не распространяется на исключения, которые могут быть выброшены вне функции Main, например, в других потоках (как отметил в других комментариях). Этот код демонстрирует, как исключение может привести к завершению работы приложения, даже если вы пытались обработать его в Main (обратите внимание, как программа корректно завершает работу, если вы нажмете Enter и разрешите приложению корректно завершить работу до возникновения исключения, но если вы позволите ему работать оканчивается довольно печально):

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

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

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

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

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception on the other thread");
   }
}
12 голосов
/ 28 июня 2010

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

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

Ой, простите, это было для winforms, для любых потоков, которые вы используете в консольном приложении, вам придется заключить в блок try / catch. Фоновые потоки, которые сталкиваются с необработанными исключениями, не приводят к завершению приложения.

0 голосов
/ 28 июня 2010

То, что вы пытаетесь, должно работать в соответствии с документацией MSDN для .Net 2.0. Вы также можете попробовать попробовать / поймать прямо в главном окне рядом с точкой входа для консольного приложения.

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

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

Отредактировано, чтобы прояснить вопрос о потоках, как указано BlueMonkMN и подробно показано в его ответе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...