Как работает SetUnhandledExceptionFilter в приложениях .NET WinForms?
Я работаю над проектом по расширению возможностей нашей производственной отладки. Наша цель состоит в том, чтобы надежно создавать minidump для любого необработанного исключения, независимо от того, управляется ли исключение или неуправляемо, и происходит ли это в управляемом или неуправляемом потоке.
Мы используем отличную библиотеку ClrDump для этого в настоящее время, но она не совсем обеспечивает точные функции, которые нам нужны, и я бы как понять механизмы фильтрации исключений, поэтому я решил попробовать это для себя.
Я начал с этой статьи блога, чтобы самостоятельно установить обработчик SEH: http://blogs.microsoft.co.il/blogs/sasha/archive/2007/12.aspx. Этот метод работает для консольных приложений, но когда я пытаюсь сделать то же самое из приложения WinForms, мой фильтр не вызывается для любого разнообразия неуправляемых исключений.
Что может сделать ClrDump, что я не делаю? ClrDump производит дампы во всех случаях, поэтому его фильтр исключения еще должен быть вызван...
Примечание. Я знаю возможности ADPlus, и мы также рассмотрели использование разделов реестра AeDebug... Это также возможности, но также имеют свои компромиссы.
Спасибо,
Dave
// Code adapted from <http://blogs.microsoft.co.il/blogs/sasha/archive/2007/12.aspx>
LONG WINAPI MyExceptionFilter(__in struct _EXCEPTION_POINTERS *ExceptionInfo)
{
printf("Native exception filter: %X\n",ExceptionInfo->ExceptionRecord->ExceptionCode);
Beep(1000,1000);
Sleep(500);
Beep(1000,1000);
if(oldFilter_ == NULL)
{
return EXCEPTION_CONTINUE_SEARCH;
}
LONG ret = oldFilter_(ExceptionInfo);
printf("Other handler returned %d\n",ret);
return ret;
}
#pragma managed
namespace SEHInstaller
{
public ref class SEHInstall
{
public:
static void InstallHandler()
{
oldFilter_ = SetUnhandledExceptionFilter(MyExceptionFilter);
printf("Installed handler old=%x\n",oldFilter_);
}
};
}
Ответы
Ответ 1
В Windows Forms встроен обработчик исключений, который по умолчанию выполняет следующие действия:
- Ловит необработанное управляемое исключение, когда:
- нет отладчика и
- Исключение возникает при обработке оконных сообщений и
- jitDebugging = false в App.Config.
- Показывает диалог для пользователя и предотвращает завершение приложения.
Вы можете отключить первое поведение, установив jitDebugging = true в App.Config. Это означает, что ваш последний шанс остановить завершение приложения - это захватить необработанное исключение, зарегистрировав для события Application.ThreadException, например. в С#:
Application.ThreadException += new Threading.ThreadExceptionHandler(CatchFormsExceptions);
Если вы решили не поймать необработанное исключение здесь, вам нужно будет проверить и/или изменить параметр реестра DbgJitDebugLaunchSetting в разделе HKLM\Software.NetFramework. Это одно из трех значений, о которых я знаю:
- 0: показывает диалоговое окно пользователя с запросом "отладка или завершение".
- 1: разрешает исключение для CLR для обработки.
- 2: запускает отладчик, указанный в разделе реестра DbgManagedDebugger.
В Visual Studio откройте "Инструменты" > "Параметры" > "Отладка" > "JIT", чтобы установить этот ключ в 0 или 2. Но на компьютере конечного пользователя обычно требуется значение 1. Обратите внимание, что этот раздел реестра действует до того, как вы обсудите событие с необработанным исключением CLR.
Затем вы можете настроить фильтр собственных исключений, который вы обсуждали.
Ответ 2
Если вы хотите, чтобы ваши исключения потоков GUI работали так же, как ваши-не-GUI, чтобы они обрабатывались одинаково, вы можете сделать это:
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
Здесь фон:
В управляемом графическом приложении по умолчанию исключения, возникающие в потоке графического интерфейса, обрабатываются тем, что назначено в Application.ThreadException, которое вы можете настроить следующим образом:
Application.ThreadException +=
new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Исключения, возникающие в других потоках, обрабатываются AppDomain.CurrentDomain.UnhandledException, которые вы можете настроить следующим образом:
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(Program.CurrentDomain_UnhandledException);
Назначение UnHandledException работает точно так же, как вызов Win32 SetUnhandledExceptionFilter.
Если вы хотите создать мини-накопители, а затем использовать их, вам потребуется использовать инструменты отладки для Windows, sos.dll. Вам нужно будет создавать мини-минипы MiniDumpWithFullMemory.
И тогда, даже тогда, вы, вероятно, не будете иметь все, что захотите. System.Diagnostics.StackTrace, чтобы получить стек управляемых вызовов.
Ответ 3
SetUnhandledExceptionFilter устанавливает обработчик, который вызывается, когда Win32-excpetion достигает вершины столов вызовов потоков без обработки.
Во многих языковых версиях, включая управляемые, исключения языка реализованы с использованием исключений Win32. Но управляемая среда выполнения будет иметь верхний уровень __try __catch (...) в верхней части каждого потока, который будет перехватывать любые исключения win32 для выполнения и обрабатывать их, не позволяя им сбежать с обработчиком верхнего уровня Win32.
Знание конкретной среды выполнения было бы необходимо для ввода обработчика на этом уровне, потому что исключениям никогда не удастся сбежать с обработчиком Win32 TheadProc.