Search code examples
c++exceptionstack-tracecallstackcrash-dumps

SEHException while in paint() - .NET handling is ruining call stack


I have error handling code that is designed to catch unnhandled exceptions, and then create a dump.

Now, i've encountered a few situations where this doesn't work well in the transition between Native and managed code.

For example if in a Paint() event I call some native code, and that native code throws an exception.
The message loop dispatcher will catch the native exception, and then rethrow a .NET SEHException.
Same thing happens sometimes with DLLS that host COM objects.

The problem with this is that because of the stack-roll back and the way it catches and creates an SEHException from the native exception, the actual Native call stack is destroyed.

Can i get the native call stack somehow, using the SEHException ? (note that the call stack in the SEHException is the CLR call stack).

Or can i set the application so it will save the call stack somehow ?


Solution

  • What happens is that .Net called Paint() when it needed to paint a window.
    Apparently .Net made this very safe and added extra precautions and error handling.

    Now my Paint() event called a C++/CLI wrapper dll that called a native C++ dll. The native C++ exception raised an Access Violation exception.

    Now the .Net caught that exception, and wrapped it up as a managed SEHException object, and I've lost all the call stack from the actual error point.

    Solution:

    We need to catch the exception before the .Net exception handling.
    The usual exception handling flow can be found on the net, but this sites gives a good example of the actual flow: Exception handlers

    It seems that the .Net catches the exceptions and wraps them before the SetUnhandledExceptionFilter handler is called.

    So, I've added a first chance exception handler with AddVectoredExceptionHandler and checked for fatal exceptions by checking the errorcode.

    //SEH exceptions have code 0xC0000xxxx
    #define SEH_TYPE (DWORD)0xC0000000L 
    //mask the last byte
    #define SEH_MASK (DWORD)0xFF000000L 
        LONG WINAPI CheckForSEHException( struct _EXCEPTION_POINTERS *lpTopLevelExceptionFilter)
        {   
            if ((lpTopLevelExceptionFilter->ExceptionRecord->ExceptionCode & SEH_MASK) == SEH_TYPE) //mask and check the last bits if it's an SEH exception
            {
                //handle exception here (create a dump or something)
                return EXCEPTION_EXECUTE_HANDLER;
            }
            return EXCEPTION_CONTINUE_SEARCH;
        }
    

    The exception codes can be found in WinBase.h, you can follow the definitions to the actual error codes. (ie. look for EXCEPTION_ACCESS_VIOLATION)