.NET Global обробник винятків у консольному застосуванні


199

Запитання: Я хочу визначити глобальний обробник винятків для необроблених винятків у моїй консольній програмі. У 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 і C # трактують події по-різному.

Відповіді:


283

Ні, це правильний спосіб зробити це. Це працювало саме так, як слід, з чого, можливо, ви можете працювати:

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)].


3
Я реалізував те, що ви запропонували тут, але не хочу виходити з програми. Я просто хочу ввійти в систему і продовжити процес (без Console.ReadLine()будь-якого іншого порушення потоку програми. Але я отримую виняток - повторне підвищення знову і знову, і знову.

3
@Shahrooz Jefri: Ви не можете продовжити, як тільки отримаєте необроблений виняток. Стек псується, і це термінал. Якщо у вас є сервер, те, що ви можете зробити в UnhandledExceptionTrapper, це перезапустити програму з тими ж аргументами командного рядка.
Стефан Штайгер

6
Це, безумовно, робить! Тут ми не говоримо про Application.ThreadException.
Ганс Пасант

4
Я думаю, що ключовим для розуміння цієї відповіді та коментарів у відповіді є усвідомлення того, що цей код демонструє зараз, щоб виявити виняток, але не повністю «обробляти» його, як це можливо, у звичайному блоці спробу / лову, коли у вас є можливість продовжити . Детальну інформацію див. У моїй іншій відповіді. Якщо ви обробляєте виняток таким чином, у вас немає можливості продовжувати працювати, коли виняток виникає в іншому потоці. У цьому сенсі можна сказати, що ця відповідь не повністю «обробляє» виняток, якщо під «обробкою» ви маєте на увазі «обробити і продовжувати працювати».
BlueMonkMN

4
Не кажучи вже про те, що існує багато технологій для роботи в різних потоках. Наприклад, у Бібліотеки паралельних завдань (TPL) є власний спосіб ловити необроблені винятки. Отже, сказати, що це працює не у всіх ситуаціях - це безглуздо, у С # немає жодного місця, але залежно від технологій, які ви використовуєте, ви можете підключитись до різних місць.
Дуг,

23

Якщо у вас є однопотоковий додаток, ви можете використовувати просту спробу / ловити в функції 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");
   }
}

TRY CATCH не працює у режимі випуску для несподіваних помилок: /
Muflix

12

Вам також потрібно обробити винятки з потоків:

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. Фонові нитки, які стикаються з необробленими винятками, не призводять до завершення програми.


1

Щойно я успадкував старий додаток консолі VB.NET і мені потрібно було створити обробник глобальних винятків. Оскільки в цьому питанні кілька разів згадується VB.NET, і він позначений тегом VB.NET, але всі інші відповіді тут є у C #, я подумав, що додам точний синтаксис і для програми VB.NET.

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

Я використовував REMмаркер коментарів замість однієї цитати тут, оскільки, як здається, переповнення стека трохи краще справляється із підсвічуванням синтаксису REM.


-13

Те, що ви намагаєтеся, повинно працювати відповідно до док. MSDN для .Net 2.0. Ви також можете спробувати "ловити" прямо в основному навколо точки входу для консольного додатка.

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

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

Відредагований, щоб уточнити пункт про нитки, як вказав BlueMonkMN та детально показаний у своїй відповіді.


1
На жаль, винятки все ще можуть бути викинуті за межі блоку Main (), на жаль. Це насправді не «спіймай усіх», як ти можеш подумати. Дивіться відповідь @Hans.
Майк Атлас

@Mike Спочатку я сказав, що так, як він це робить, правильно, і що він може спробувати спробувати / ловити в основному. Я не впевнений, чому ви (чи хтось інший) дали мені голос, коли я погодився з Гансом, просто давши ще одну відповідь, на яку я не сподівався отримати чек. Це насправді не справедливо, і тоді говорити про альтернативу невірно, не надаючи жодних доказів того, як виняток, який може бути спійманий процесом AppDomain UnhandledException, який спроба вловлювати в Main не може. Я вважаю грубим сказати, що щось не так, не доводячи, чому це неправильно, просто кажу, що так, не так.
Родні С. Фолі

5
Я розмістив приклад, про який ви просите. Будьте відповідальні та видаліть свої невідповідні голоси зі старих відповідей Майка, якщо ви цього не зробили. (Ніякого особистого інтересу, просто не подобається бачити такі зловживання системою.)
BlueMonkMN

3
Тим не менш, ти все ще граєш у ту саму «гру», що і він, тільки гіршим чином, оскільки це чиста помста, а не заснована на якості відповіді. Це не спосіб вирішити проблему, лише погіршити її. Особливо погано, коли ти мстишся тому, хто навіть мав законне занепокоєння щодо твоєї відповіді (як я продемонстрував).
BlueMonkMN

3
О, я також додав би, що голосування не призначене для людей, які "є повним ідіотцем або порушують правила", а скоріше судити про якість відповіді. Мені здається, що відповіді, які не мають права голосу, щоб “коментувати” особу, яка їх надає, є набагато більшим зловживанням, ніж відповіді, які не мають права голосу, залежно від змісту самої відповіді, незалежно від того, точне це голосування чи ні. Не сприймайте / не робіть це так особисто.
BlueMonkMN
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.