SetUnhandledExceptionFilter如何在.NET WinForms应用程序中工作?

时间:2008-10-24 12:15:49

标签: c# .net debugging clrdump

我正在开展一个项目来增强我们的生产调试功能。我们的目标是在任何未处理的异常上可靠地生成minidump,无论是管理异常还是非托管异常,以及它是否发生在托管或非托管线程上。

我们目前使用优秀的ClrDump库,但它并没有完全提供我们需要的确切功能,而且我想了解异常过滤背后的机制,所以我开始试试这个自己。

我首先关注这篇博客文章,自己安装SEH处理程序:http://blogs.microsoft.co.il/blogs/sasha/archive/2007/12.aspx。这种技术适用于控制台应用程序,但是当我从WinForms应用程序中尝试相同的操作时,我的过滤器不会被调用任何种类的非托管异常。

ClrDump可以做什么,我不做什么? ClrDump在所有情况下都会生成转储,因此仍必须调用其异常过滤器...

注意:我知道ADPlus的功能,我们也考虑过使用AeDebug注册表键......这些也是可能的,但也需要权衡。

谢谢, 戴夫

// 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_);
      }


   };
}

3 个答案:

答案 0 :(得分:9)

Windows窗体有一个内置的异常处理程序,默认情况下执行以下操作:

  • 在以下情况下捕获未处理的托管例外:
    • 未附加调试器,
    • 在窗口消息处理期间发生
    • 异常,
    • app.Config中的jitDebugging = false。
  • 向用户显示对话框并阻止应用终止。

您可以通过在App.Config中设置 jitDebugging = true 来禁用第一个行为。这意味着你停止应用程序终止的最后一次机会是通过注册事件Application.ThreadException来捕获未处理的异常,例如,在C#中:

Application.ThreadException += new Threading.ThreadExceptionHandler(CatchFormsExceptions);

如果您决定不在此处捕获未处理的异常,则需要检查和/或更改HKLM \ Software.NetFramework下的注册表设置 DbgJitDebugLaunchSetting 。这是我所知道的三个值中的一个:

  • 0:显示用户对话框询问“调试或终止”。
  • 1:让CLR处理异常。
  • 2:启动DbgManagedDebugger注册表项中指定的调试器。

在Visual Studio中,转到工具&gt;选项&gt;调试&gt; JIT将此键设置为0或2.但值1通常是您在最终用户的计算机上所需的值。请注意,在您讨论的CLR未处理的异常事件之前,将对此注册表项执行操作。

然后,您可以设置您讨论过的本机异常过滤器。

答案 1 :(得分:4)

如果您希望GUI线程异常像非GUI GUI一样工作,以便以相同的方式处理它们,您可以这样做:

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

以下是背景资料:

在manged GUI应用程序中,默认情况下,源自GUI线程的异常由分配给Application.ThreadException的任何内容处理,您可以像这样自定义:

Application.ThreadException += 
    new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);

源自其他线程的异常由AppDomain.CurrentDomain.UnhandledException处理,您可以像这样自定义:

AppDomain.CurrentDomain.UnhandledException += 
    new UnhandledExceptionEventHandler(Program.CurrentDomain_UnhandledException);

分配给UnHandledException就像调用Win32 SetUnhandledExceptionFilter一样。

如果您的目标是创建minidump然后使用它们,则需要使用Windows调试工具sos.dll。你需要生成minidumps MiniDumpWithFullMemory。

然后,即便如此,你可能也不会拥有你想要的一切。 System.Diagnostics.StackTrace获取调用托管调用堆栈。

答案 2 :(得分:2)

SetUnhandledExceptionFilter安装一个处理程序,当Win32-excpetion到达线程调用堆栈的顶部而不进行处理时调用该处理程序。

在许多语言运行时(包括托管)中,使用Win32异常实现语言异常。但是,托管运行时将在每个线程的顶部有一个顶级__try __catch(...)块,它将捕获任何win32到运行时异常并处理它们,而不让它们转义到Win32的顶级处理程序。

在此级别注入处理程序需要了解特定的运行时,因为永远不允许异常转义到Win32的TheadProc处理程序。