Как работает 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.