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

时间:2021-04-23 05:51:05

I am working on a project to enhance our production debugging capabilities. Our goal is to reliably produce a minidump on any unhandled exception, whether the exception is managed or unmanaged, and whether it occurs on a managed or unmanaged thread.

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

We use the excellent ClrDump library for this currently, but it does not quite provide the exact features we need, and I'd like to understand the mechanisms behind exception filtering, so I set out to try this for myself.

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

I started out by following this blog article to install an SEH handler myself: http://blogs.microsoft.co.il/blogs/sasha/archive/2007/12.aspx. This technique works for console applications, but when I try the same thing from a WinForms application, my filter is not called for any variety of unmanaged exceptions.

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

What can ClrDump be doing that I'm not doing? ClrDump produces dumps in all cases, so its exception filter must still be called...

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

Note: I'm aware of ADPlus's capabilities, and we've also considered using the AeDebug registry keys... These are also possibilities, but also have their tradeoffs.

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

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


   };
}

3 个解决方案

#1


9  

Windows Forms has a built-in exception handler that does the following by default:

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

  • Catches an unhandled managed exception when:
    • no debugger attached, and
    • 没有附加调试器,和

    • exception occurs during window message processing, and
    • 窗口消息处理期间发生异常,并且

    • jitDebugging = false in App.Config.
    • App.Config中的jitDebugging = false。

  • 在以下情况下捕获未处理的托管异常:未附加调试器,窗口消息处理期间发生异常,App.Config中发生jitDebugging = false。

  • Shows dialog to user and prevents app termination.
  • 向用户显示对话框并阻止应用终止。

You can disable the first behaviour by setting jitDebugging = true in App.Config. This means that your last chance to stop the app terminating is to catch the unhandled exception by registering for the event Application.ThreadException, e.g. in C#:

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

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

If you decide not to catch the unhandled exception here, then you will need to check and/or change the registry setting DbgJitDebugLaunchSetting under HKLM\Software.NetFramework. This has one of three values of which I'm aware:

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

  • 0: shows user dialog asking "debug or terminate".
  • 0:显示询问“调试或终止”的用户对话框。

  • 1: lets exception through for CLR to deal with.
  • 1:让CLR处理异常。

  • 2: launches debugger specified in DbgManagedDebugger registry key.
  • 2:启动DbgManagedDebugger注册表项中指定的调试器。

In Visual Studio, go to Tools>Options>Debugging>JIT to set this key to 0 or 2. But a value of 1 is usually what you want on an end-user's machine. Note that this registry key is acted on before the CLR unhandled exception event that you discuss.

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

Then you can set the native exception filter that you discussed.

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

#2


4  

If you want your GUI thread exceptions to work just like your-non GUI ones, so that they get handled the same way, you can do this:

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

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

Here's the background:

这是背景:

In a manged GUI app, by default, exceptions that originate in the GUI thread are handled by whatever is assigned to the Application.ThreadException, which you can customize like this:

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

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

Exceptions that originate in the other threads are handled by AppDomain.CurrentDomain.UnhandledException, which you can customize like this:

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

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

Assigning to UnHandledException works exactly like calling Win32 SetUnhandledExceptionFilter.

分配给UnHandledException的工作方式与调用Win32 SetUnhandledExceptionFilter完全相同。

If you goal is to create minidumps and then use them, you'll need to use Debugging Tools for Windows, sos.dll. You'll need to produce minidumps MiniDumpWithFullMemory.

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

And then, even then, you probably won't have everything you might want. System.Diagnostics.StackTrace to get the call managed call stack.

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

#3


2  

SetUnhandledExceptionFilter installs a handler that is invoked when a Win32-excpetion reaches the top of a threads callstack without being handled.

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

In many language runtimes including managed, language exceptions are implemented using Win32 exceptions. But, the managed runtime will have a top level __try __catch(...) block at the top of each thread that will catch any win32 to runtime exceptions and process them without letting them escape to Win32's top level handler.

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

Knowledge of the specific runtime would be necessary to inject a handler at this level because the exceptions will never be allowed to escape to Win32's TheadProc handler.

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

#1


9  

Windows Forms has a built-in exception handler that does the following by default:

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

  • Catches an unhandled managed exception when:
    • no debugger attached, and
    • 没有附加调试器,和

    • exception occurs during window message processing, and
    • 窗口消息处理期间发生异常,并且

    • jitDebugging = false in App.Config.
    • App.Config中的jitDebugging = false。

  • 在以下情况下捕获未处理的托管异常:未附加调试器,窗口消息处理期间发生异常,App.Config中发生jitDebugging = false。

  • Shows dialog to user and prevents app termination.
  • 向用户显示对话框并阻止应用终止。

You can disable the first behaviour by setting jitDebugging = true in App.Config. This means that your last chance to stop the app terminating is to catch the unhandled exception by registering for the event Application.ThreadException, e.g. in C#:

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

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

If you decide not to catch the unhandled exception here, then you will need to check and/or change the registry setting DbgJitDebugLaunchSetting under HKLM\Software.NetFramework. This has one of three values of which I'm aware:

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

  • 0: shows user dialog asking "debug or terminate".
  • 0:显示询问“调试或终止”的用户对话框。

  • 1: lets exception through for CLR to deal with.
  • 1:让CLR处理异常。

  • 2: launches debugger specified in DbgManagedDebugger registry key.
  • 2:启动DbgManagedDebugger注册表项中指定的调试器。

In Visual Studio, go to Tools>Options>Debugging>JIT to set this key to 0 or 2. But a value of 1 is usually what you want on an end-user's machine. Note that this registry key is acted on before the CLR unhandled exception event that you discuss.

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

Then you can set the native exception filter that you discussed.

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

#2


4  

If you want your GUI thread exceptions to work just like your-non GUI ones, so that they get handled the same way, you can do this:

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

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

Here's the background:

这是背景:

In a manged GUI app, by default, exceptions that originate in the GUI thread are handled by whatever is assigned to the Application.ThreadException, which you can customize like this:

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

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

Exceptions that originate in the other threads are handled by AppDomain.CurrentDomain.UnhandledException, which you can customize like this:

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

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

Assigning to UnHandledException works exactly like calling Win32 SetUnhandledExceptionFilter.

分配给UnHandledException的工作方式与调用Win32 SetUnhandledExceptionFilter完全相同。

If you goal is to create minidumps and then use them, you'll need to use Debugging Tools for Windows, sos.dll. You'll need to produce minidumps MiniDumpWithFullMemory.

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

And then, even then, you probably won't have everything you might want. System.Diagnostics.StackTrace to get the call managed call stack.

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

#3


2  

SetUnhandledExceptionFilter installs a handler that is invoked when a Win32-excpetion reaches the top of a threads callstack without being handled.

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

In many language runtimes including managed, language exceptions are implemented using Win32 exceptions. But, the managed runtime will have a top level __try __catch(...) block at the top of each thread that will catch any win32 to runtime exceptions and process them without letting them escape to Win32's top level handler.

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

Knowledge of the specific runtime would be necessary to inject a handler at this level because the exceptions will never be allowed to escape to Win32's TheadProc handler.

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