Обработчик исключений .NET Global в консольном приложении
Вопрос: Я хочу определить глобальный обработчик исключений для необработанных исключений в моем консольном приложении. В asp.net можно определить один в global.asax, а в приложениях/службах Windows можно определить как ниже
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);
Но как я могу определить глобальный обработчик исключений для консольного приложения?
currentDomain, похоже, не работает (.NET 2.0)?
Изменить:
Арг, глупая ошибка.
В VB.NET нужно добавить ключевое слово "AddHandler" перед currentDomain, иначе никто не увидит событие UnhandledException в IntelliSense...
Это потому, что компиляторы VB.NET и С# обрабатывают обработку событий по-разному.
Ответы
Ответ 1
Нет, это правильный способ сделать это. Это работало точно так, как должно, от чего-то, от чего вы можете работать:
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)].
Ответ 2
Если у вас однопоточное приложение, вы можете использовать простой try/catch в главной функции, однако это не распространяется на исключения, которые могут быть выбрасываться за пределы основной функции, например, на другие потоки (как отмечено в других комментариях). Этот код демонстрирует, как исключение может привести к завершению работы приложения, даже если вы пытались обработать его в 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");
}
}
Ответ 3
Вам также нужно обрабатывать исключения из потоков:
static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}
private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
Console.WriteLine(e.Exception.StackTrace);
}
Whoop, извините, что это было для winforms, для любых потоков, которые вы используете в консольном приложении, вам придется заключить в блок try/catch. Фоновые потоки, которые сталкиваются с необработанными исключениями, не приводят к завершению работы приложения.
Ответ 4
То, что вы пытаетесь, должно работать в соответствии с документом MSDN для .Net 2.0. Вы также можете попробовать попробовать/поймать в основном вокруг своей точки входа для консольного приложения.
static void Main(string[] args)
{
try
{
// Start Working
}
catch (Exception ex)
{
// Output/Log Exception
}
finally
{
// Clean Up If Needed
}
}
И теперь ваш улов будет обрабатывать все, что не поймано (в основном потоке). Он может быть изящным и даже перезагружаться там, где он был, если вы хотите, или вы можете просто позволить программе умереть и зарегистрировать исключение. Вы, наконец, добавите, если хотите очистить. Для каждого потока потребуется собственное управление исключениями на высоком уровне, подобное основному.
Отредактировано, чтобы прояснить вопрос о потоках, как указано BlueMonkMN и подробно показано в его ответе.