Подавление 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, которое будет разговаривать с вашим новым сервисом и запускает/останавливает его.