Подавление Application.ThreadException и AppDomain.CurrentDomain.UnhandledException

Я написал приложение WinForms, которое выполняет какую-то синхронизацию. Я хочу, чтобы он продолжал работать, несмотря на любые случаи исключений, чтобы синхронизация продолжала идти со следующими попытками. Таким образом, я написал приложение таким образом, что всякий раз, когда возникает исключение, он регистрирует трассировку стека исключений, выполняет некоторую диагностику, затем записывает диагностическую информацию и продолжает следующие попытки синхронизации.

Для неперехваченных исключений я добавил обработчики исключений для Application.ThreadException, а также для AppDomain.CurrentDomain.UnhandledException в потоке Main():

static void Main(string[] args)
{    
    Application.ThreadException += new ThreadExceptionEventHandler(UIThreadExceptionHandler);
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(UnhandledExceptionHandler);


    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new myForm());
}


//exception handlers
 public static void UIThreadExceptionHandler(object sender, ThreadExceptionEventArgs t)
 {
     logger.Fatal("Fatal Windows Forms Error");
     logStackTrace(t.Exception);                    //logs the stack trace with some desired formatting  
     Application.Exit(new CancelEventArgs(true));
 }

 public static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs t) 
 {
     logger.Fatal("Fatal Application Error");
     logStackTrace((Exception)(t.ExceptionObject)); //logs the stack trace with some desired formatting           
     Application.Exit(new CancelEventArgs(true));
 }

Я поддерживал приложение для тестирования и понял, что всякий раз, когда Application.ThreadException поймается, приложение корректно регистрирует строку Fatal Windows Forms Error, за которой следует трассировка стека исключений, а затем приложение завершает работу.

Я не хочу, чтобы приложение выходило. Я просто хочу, чтобы он регистрировал трассировку стека исключений и продолжал следующую попытку синхронизации. Исключение, происходящее в потоке пользовательского интерфейса, не является столь серьезным, поскольку одно и то же исключение произошло и в других/не-пользовательских потоках приложения, но оно не разбило приложение (как я вижу это в журналах). Однако, когда такое же исключение возникает в потоке пользовательского интерфейса, приложение вылетает.

Ответ 1

Поскольку это приложение Windows Form создает приложение Application.ThreadException, используется для исключения без исключения: "Это событие позволяет вашему приложению Windows Forms обрабатывать иначе необработанные исключения, возникающие в потоках Windows Forms. Прикрепите обработчики событий к событию ThreadException, чтобы справиться с этим исключением" (MSDN)

Я думаю, вы должны ожидать такого поведения, как у UIThreadExceptionHandler, у вас есть Application.Exit(новый CancelEventArgs (true)). Описание методов выхода: "Сообщает все насосы сообщений, которые они должны завершать, а затем закрывает все окна приложений после обработки сообщений". (MSDN)

Событие AppDomain.CurrentDomain.UnhandledException используется для обработки исключений потоков, отличных от UI.

РЕДАКТИРОВАТЬ 1:

AppDomain.CurrentDomain.UnhandledException специально предназначен для регистрации исключения до того, как система сообщит пользователю и завершит процесс. Вы не можете предотвратить завершение процесса (если вы не используете Net 1.1).

Application.ThreadException + UnhandledExceptionMode.CatchException позволяет сохранить поток пользовательского интерфейса. Но это действительно не очень хорошая идея. Лучше всего заключить код с ошибкой в ​​блоки try-catch.

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

У вас не проблема с UnhandledExceptionHandler, потому что она вообще не запускается, я полагаю.

Ответ 2

Windows Forms на самом деле не предназначен для сценария, в котором приложение должно запускаться неограниченное время. Как вы уже убедились, к тому времени, как вы перехватили определенные исключения, ваше приложение уже всасывается в черную дыру, и вы не можете остановить свое приложение.

Я думаю, что лучший подход - использовать службу Windows, которая имеет два потока: поток монитора переднего плана и поток рабочего стола (используйте тип BackgroundWorker). Рабочий поток - это то, где происходит ваша синхронизация. Когда рабочий поток умирает из-за необработанного исключения, поток монитора регистрирует исключение и перезапускает рабочий поток.

Если ситуация достаточно противная, что поток монитора также умирает (например, OutOfMemoryException или ExecutionEngineException), тогда сама служба умрет. Но вы можете настроить диспетчер управления сервисом для перезапуска службы в этом случае.

Если вам нужна какая-то пользовательская интерактивность, вы также можете создать приложение Windows Forms, которое будет разговаривать с вашим новым сервисом и запускает/останавливает его.