.NET - Який найкращий спосіб реалізувати “обробник всіх винятків”


75

Мені цікаво, що найкращий спосіб - це "якщо все інше не вдасться зрозуміти".

Я маю на увазі, що ви обробляєте якомога більше винятків у своїй програмі, але все-таки обов’язково мають бути помилки, тому мені потрібно мати щось, що вловлює всі необроблені винятки, щоб я міг збирати інформацію та зберігати їх у базі даних або подавати до веб-служби.

Чи захоплює все подія AppDomain.CurrentDomain.UnhandledException? Навіть якщо додаток багатопотоковий?

Примітка: Windows Vista надає власні функції API, які дозволяють будь-якій програмі відновити себе після аварії ... не можу зараз придумати назву ... але я волію не використовувати її, оскільки багато наших користувачів все ще використовують Windows XP.


2
Це функція "Диспетчер перезапуску" в Windows Vista: danielmoth.com/Blog/2006/10/vista-restart-manager.html
huseyint


Відповіді:


33

Я щойно грав із поведінкою AppDomain UnhandledException ((це остання стадія, на якій зареєстровано необроблене виняток)

Так, після обробки обробників подій ваша програма буде припинена, і буде показано неприємне діалогове вікно "... програма перестала працювати".

:) Ви все ще можете цього уникнути.

Перевіряти:

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();
    }
}

PS Обробляйте необроблені для Application.ThreadException (WinForms) або DispatcherUnhandledException (WPF) на вищому рівні.


Обробка DispatcherUnhandledException вирішила мої проблеми, хоча забула оновити питання. Thnx за відповідь
TimothyP

21
Думаю, справедливо зауважити, що наведений вище код назавжди залишає кожну невдалу нитку. Тобто рано чи пізно у вас буде безліч зомбі-ниток. Особисто я не вважаю це дуже корисним.
Брайан Расмуссен,

2
Я згоден. Це породило зомбі. На жаль, я приймаю цю альтернативу вбивству моєї програми відразу після виходу обробника. Зрештою, перед тим, як дозволити мертвій нитці назавжди спати, ви можете надіслати звіт на свій сервер, дозволити користувачеві закінчити завдання і вийти (або перезапустити програму) витончено.
bohdan_trotsenko

3
Це рішення є настільки неправильним і водночас настільки необхідним за певних обставин :) +1
empi

1
Це викине якщо ім'я потоку вже встановлено раніше: Thread.CurrentThread.Name = "Dead thread".
FunctorSalad

17

В ASP.NET ви використовуєте Application_Errorфункцію у Global.asaxфайлі.

У WinForms ви використовуєте файл MyApplication_UnhandledExceptionу ApplicationEventsфайлі

Обидві ці функції викликаються, якщо у вашому коді трапляється необроблена виняток. Ви можете зареєструвати виняток і подати приємне повідомлення користувачеві з цих функцій.


13

Для додатків Winform, крім AppDomain.CurrentDomain.UnhandledException, я також використовую Application.ThreadException та Application.SetUnhandledExceptionMode (з / UnhandledExceptionMode.CatchException). Здається, ця комбінація вловлює все.


6
все, крім винятків для вторинних потоків, потоків таймера та потоків пулу потоків!
Стівен А. Лоу,

2
Я погоджуюся, що якщо під "обробкою винятків" розуміють, що насправді потрібно щось відновити після цього, "UnhandledException" AppDomain недостатньо. Однак оригінальне запитання було спрямоване лише на повідомлення про помилки, а не на відновлення. Тільки для цієї мети цього було б достатньо.
Крістіан.

1
@Steven: Якщо завдання пулу потоків запускається через BeginInvoke, будь-яке виняток із цього потоку перетворюється на викличний потік, коли викликається EndInvoke.
Брайан Расмуссен,

6

У головному потоці у вас є такі опції:

Для інших потоків:

  • Вторинні потоки не мають оброблених винятків; використовувати SafeThread
  • Робочі нитки: (таймер, пул ниток) захисної мережі взагалі немає!

Майте на увазі, що ці події не обробляють винятки, вони просто повідомляють про них програмі - часто, коли занадто пізно робити щось корисне / розумне щодо них

Реєстрація винятків - це добре, але моніторинг програм краще ;-)

Застереження: Я є автором статті SafeThread .


@ StevenA.Lowe не є повною резервною копією вихідного коду про СПОКУНТ?
Кікенет,

4

Що стосується WinForms, не забудьте приєднатись до поточної непереробленої події винятків (особливо якщо ви використовуєте багатопоточність).

Деякі посилання на найкращі практики тут і тут, і тут (мабуть, найкраща стаття щодо обробки винятків для .net)


будь ласка, посилання на посилання на "поточну неперероблену виняткову подію", у мене склалося враження, що такого не було!
Стівен А. Лоу,


2

Існує також класна річ під назвою ELMAH, яка реєструє будь-які помилки ASP.NET, які виникають у веб-програмі. Я знаю, що ви запитуєте про рішення Winform App, але я вважав, що це може бути корисним для тих, хто потребує такого типу речей у веб-програмі. Ми використовуємо його там, де я працюю, і це було дуже корисно при налагодженні (особливо на робочих серверах!)

Ось декілька функцій, які він має (витягнути прямо зі сторінки):

  • Протоколювання майже всіх необроблених винятків.
  • Веб-сторінка для віддаленого перегляду всього журналу перекодованих винятків.
  • Веб-сторінка для віддаленого перегляду всіх деталей будь-якого зареєстрованого винятку.
  • У багатьох випадках ви можете переглянути оригінальний жовтий екран смерті, який ASP.NET створив для даного винятку, навіть із вимкненим режимом customErrors.
  • Повідомлення електронною поштою про кожну помилку в момент її виникнення.
  • RSS-канал із останніми 15 помилками з журналу.
  • Ряд резервних реалізацій сховища для журналу, включаючи вбудовану пам'ять, Microsoft SQL Server та кілька внесених спільнотою.

Немає значення, що я запитував про не веб-програми, це все одно досить корисна інформація для інших! так +1
Тимофій

2

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


0

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

try 
{
    CallTheCodeThatMightThrowException()
 }
catch (Exception ex)
{
    System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace ();
    Utils.ErrorHandler.Trap ( ref objUser, st, ex );
} //eof catch

І ось код ErrorHandler: Просто для роз'яснення-: objUser - це об'єкт, що моделює аппусатори (ви можете отримати таку інформацію, як ім'я домену, відділ, регіон тощо для цілей реєстрації ILog logger - це об'єкт реєстрації - наприклад, той, що виконання журналювання StackTrace st - об’єкт StackTrace, що надає інформацію про налагодження вашого додатка

using System;
using log4net; //or another logging platform

namespace GenApp.Utils
{
  public class ErrorHandler
  {
    public static void Trap ( Bo.User objUser, ILog logger, System.Diagnostics.StackTrace st, Exception ex )
    {
      if (ex is NullReferenceException)
      { 
      //do stuff for this ex type
      } //eof if

      if (ex is System.InvalidOperationException) 
      {
        //do stuff for this ex type
      } //eof if

      if (ex is System.IndexOutOfRangeException) 
      {
        //do stuff for this ex type
      } //eof if

      if (ex is System.Data.SqlClient.SqlException)
      {
        //do stuff for this ex type
      } //eof if

      if (ex is System.FormatException)
      {
        //do stuff for this ex type
      } //eof if

      if (ex is Exception)
      {
        //do stuff for this ex type
      } //eof catch

    } //eof method 

  }//eof class 
} //eof namesp

Це вловить лише винятки для поточного потоку. Тобто мало корисно, якщо CallTheCodeThatMightThrowException створює або використовує інші потоки.
Брайан Расмуссен

0

У керованому програмі графічного інтерфейсу за замовчуванням винятки, що походять із потоку графічного інтерфейсу, обробляються тим, що присвоєно Application.ThreadException.

Винятки, що походять з інших потоків, обробляються AppDomain.CurrentDomain.UnhandledException.

Якщо ви хочете, щоб винятки з потоку графічного інтерфейсу працювали так само, як і ті, що не стосуються графічного інтерфейсу, щоб їх обробляв AppDomain.CurrentDomain.UnhandledException, ви можете зробити це:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

Перевагою лову винятків потоків графічного інтерфейсу за допомогою ThreadException є те, що ви можете надати користувачеві можливості дозволити додатку продовжуватись. Щоб переконатися, що жоден конфігураційний файл не замінює поведінку за замовчуванням, можна зателефонувати:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

Ви все ще вразливі до винятків із погано поведених рідних dll. Якщо рідна dll встановлює власний обробник за допомогою Win32 SetUnhandledExceptionFilter, передбачається зберегти вказівник на попередній фільтр і також викликати його. Якщо цього не відбувається, ваш обробник не буде дзвонити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.