10

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

我们目前为此使用了出色的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_);
      }


   };
}
4

3 回答 3

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 中,转到 Tools>Options>Debugging>JIT 将此键设置为 0 或 2。但值 1 通常是您在最终用户计算机上想要的值。请注意,此注册表项是在您讨论的 CLR 未处理异常事件之前执行的。

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

于 2008-10-26T17:55:01.577 回答
4

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

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

这是背景:

在托管的 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 完全相同。

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

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

于 2009-09-26T18:15:43.100 回答
2

SetUnhandledExceptionFilter 安装一个处理程序,当 Win32 异常到达线程调用堆栈的顶部而没有被处理时调用该处理程序。

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

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

于 2008-10-24T14:22:30.627 回答