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