UnhandledExceptionHandler на C# вне Visual Studio

166
17 ноября 2018, 01:00

Есть проект. Начальство, спустя более 40к строк кода решило добавить логирование. Обычное, уровня Debug, уже добавил, теперь пришёл черед Exception-ов.

Есть вариант прошить всё try {} catch {log.write(message)}, но, по-моему, это не самый оптимальный вариант.

Попытался использовать UnhandledExceptionHandler, однако вне студии (да и банально в запуске без отладки) handler работать не хочет.

Вопрос: не сталкивался ли кто с подобным и что можете посоветовать в таком случае? Т.е. задача: перехватывать все (не только фатальные) исключения в релизе.

Приложение WinForms .NET 2.0 (да, всё настолько плохо:)

Добавлено:

Вешаю Handler в Main перед Application.Run(mainForm):

AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

Код обработки

static void UnhandledExceptionHandler(object source, UnhandledExceptionEventArgs e)
        {
            logger.Error("Message: " + ((Exception)e.ExceptionObject).Message);
            var st = new StackTrace(((Exception)e.ExceptionObject), true);
            for (int i = 0; i < st.FrameCount; i++)
            {
                var frame = st.GetFrame(i);
                var method = frame.GetMethod();
                //if (method.DeclaringType.FullName.Contains("AMT"))
                {
                    logger.Error("Source: " + method.DeclaringType.FullName + "." + method.Name);
                    logger.Error("Line: " + frame.GetFileLineNumber());
                }
            }
        }

Код просто не отрабатывает, как будто его и нет. В самой студии в режиме отладки всё нормально - и брекпоинт можно поставить, и лог пишется. Как только запускаю .exe напрямую - метода как-будто нет.

Обновление

Решение проблемы:

В Main:

AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
Application.ThreadException += new ThreadExceptionEventHandler(ThreadExceptionHandler);

И три метода, два из которых перехватывают исключения и один вспомогательный для вывода инфы для юзера.

static void UnhandledExceptionHandler(object source, UnhandledExceptionEventArgs e)
        {
            logger.Error("Message: " + ((Exception)e.ExceptionObject).Message);
            var st = new StackTrace(((Exception)e.ExceptionObject), true);
            for (int i = 0; i < st.FrameCount; i++)
            {
                var frame = st.GetFrame(i);
                var method = frame.GetMethod();
                if (method.DeclaringType.FullName.Contains("AMT"))
                {
                    logger.Error("Source: " + method.DeclaringType.FullName + "." + method.Name);
                    logger.Error("Line: " + frame.GetFileLineNumber());
                }
            }
        }
        private static void ThreadExceptionHandler(object sender, ThreadExceptionEventArgs e)
        {
            logger.Error("Message: " + e.Exception.Message);
            var st = new StackTrace(e.Exception, true);
            for (int i = 0; i < st.FrameCount; i++)
            {
                var frame = st.GetFrame(i);
                var method = frame.GetMethod();
                if (method.DeclaringType.FullName.Contains("AMT"))
                {
                    logger.Error("Source: " + method.DeclaringType.FullName + "." + method.Name);
                    logger.Error("Line: " + frame.GetFileLineNumber());
                }
            }
            DialogResult result = DialogResult.Cancel;
            try
            {
                result = ShowThreadExceptionDialog("Windows Forms Error", e.Exception);
            }
            catch
            {
                try
                {
                    MessageBox.Show("Fatal Windows Forms Error", "Fatal Windows Forms Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
                }
                finally
                {
                    Application.Exit();
                }
            }
            if (result == DialogResult.Abort)
                Application.Exit();
        }
        private static DialogResult ShowThreadExceptionDialog(string title, Exception e)
        {
            string errorMsg = "An application error occurred. Please contact the adminstrator " +
                "with the following information:\n\n";
            errorMsg = errorMsg + e.Message + "\n\nStack Trace:\n" + e.StackTrace;
            return MessageBox.Show(errorMsg, title, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop);
        }
Answer 1

Подпишитесь на Application.ThreadException

Исключения из ГУИ-потока так ловятся. А вот исключения из прочих потоков, просто крашат приложение, поэтому их пришлось завернуть в try-catch все.

Answer 2

У меня приведенный код не работает ни под отладчиком, ни без него. Дело в том, что режим UnhandledExceptionMode.CatchException перенаправляет исключение стандартному обработчику потока (который для основного потока WinForms выводит знаменитое сообщение с красным крестом), и до вашего обработчика ничего не доходит. Замена на UnhandledExceptionMode.ThrowException решает проблему.

READ ALSO
Не открывается .xlsx в datagrid

Не открывается .xlsx в datagrid

Возникла проблема с WPF: в приложении у меня при нажатии кнопки должен открыться файл с расширениемxlsx и его содержимое перенестись в DataGrid,...

173
Не работает async|await в visual studio 2010

Не работает async|await в visual studio 2010

В 2010 студии не работает async | awaitКак показали зарубежные источники, не хватает обновления (т е библиотеки)

243