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.
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.
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.
What can ClrDump be doing that I'm not doing? ClrDump produces dumps in all cases, so its exception filter must still be called...
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.
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_);
}
};
}
Windows Forms has a built-in exception handler that does the following by default:
- Catches an unhandled managed exception when:
- no debugger attached, and
- exception occurs during window message processing, and
- jitDebugging = false in App.Config.
- 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#:
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:
- 0: shows user dialog asking "debug or terminate".
- 1: lets exception through for CLR to deal with.
- 2: launches debugger specified in DbgManagedDebugger registry key.
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.
Then you can set the native exception filter that you discussed.
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:
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:
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 +=
new UnhandledExceptionEventHandler(Program.CurrentDomain_UnhandledException);
Assigning to UnHandledException works exactly like calling 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.
And then, even then, you probably won't have everything you might want. System.Diagnostics.StackTrace to get the call managed call stack.
SetUnhandledExceptionFilter installs a handler that is invoked when a Win32-excpetion reaches the top of a threads callstack without being handled.
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.
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.