Sunday, December 12, 2010

c++/CLR/SEH/MFC/VEH exception notes (continuously updating)

  • SEH/VEH

A new interesting topic is VEH (vector exception handler), which is introduced since windows XP. The difference is as below:

    • Not like SEH, which is saved in the TEB, is popped as stack unwinds and only valid in the current thread; VEH is saved in the global heap, which is effective for the whole process;
    • All VEH will be executed before SEH
    • VEH only exists in the user-mode codes, SEH can be both user/kernel modes
    • SEH is registered/revoked by the codes generated by compiler, VEH is registered/revoked by system API:

AddVectoredExceptionhandler

RemoveVectoredExceptionHandler

    • The signature for VEH is:

LONG CALLBACK VectorHandler(PEXCEPTION_POINTERS ExceptionInfo)

  • C++/CLR/SEH exception
    • relationship

SEH exception means structured exception handler, CLR exception and C++ exception are two special SEH one, each has its own SEH code. C++ exception code is: 0xe06d7363 and CLR is 0xe0434f4d. C++ exception keywords are try/catch, SEH is __try/__except, MFC is the TRY/CATCH macros, built on the top of SEH keywords. Microsoft strongly suggests converting usage of MFC exceptions to C++ ones, and also provides some samples about how to convert correctly.

http://msdn.microsoft.com/en-us/library/19z28s5c.aspx

    • 64-bit windows no longer saves exception handler on the stack

In 64-bit windows, the exception handler is no longer saved on the stack, instead an exception handler table is defined to hold them.

    • translate SEH to c++ exception

SEH, also can be called as c exception, not like c++ one, only deal with integer, and c++ exception is based on the types. We can also use _set_se_translator to enable the translation from SEH exception to c++ one. For instance:

   1: // from msdn http://msdn.microsoft.com/en-us/library/5z4bw5h5%28v=VS.90%29.aspx
   2: // crt_settrans.cpp
   3: // compile with: /EHa
   4: #include <stdio.h>
   5: #include <windows.h>
   6: #include <eh.h>
   7:  
   8: void SEFunc();
   9: void trans_func( unsigned int, EXCEPTION_POINTERS* );
  10: class SE_Exception
  11: {
  12: private:
  13:     unsigned int nSE;
  14: public:
  15:     SE_Exception() {}
  16:     SE_Exception( unsigned int n ) : nSE( n ) {}
  17:     ~SE_Exception() {}
  18:     unsigned int getSeNumber() { return nSE; }
  19: };
  20: int main( void )
  21: {
  22:     try
  23:     {
  24:         _set_se_translator( trans_func );
  25:         SEFunc();
  26:     }
  27:     catch( SE_Exception e )
  28:     {
  29:         printf( "Caught a __try exception with SE_Exception.\n" );
  30:     }
  31: }
  32:  
  33: void SEFunc()
  34: {
  35:     __try
  36:     {
  37:         int x, y=0;
  38:         x = 5 / y;
  39:     }
  40:     __finally
  41:     {
  42:         printf( "In finally\n" );
  43:     }
  44: }
  45: void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp )
  46: {
  47:     printf( "In trans_func.\n" );
  48:     throw SE_Exception();
  49: }


  • Async and Sync exception

We often mention async and sync exception models, since for the async case, there is no explicit statement throwing the exception, for the sync, we can always find kernel32!RaiseException on the callstack. The basic SEH is the async model, for instance, processor thrown when finding access violation.


Visual studio can specify two exception models, /Eha and /Ehsc, for async and sync mode respectively. CLR always assumes /Eha, native C++ program can use either of them. In the mixed programming, when exception thrown from native, caught in managed, different exception models can cause trouble (refer to my another post). When specifying async mode, c++ program can also deal with SEH ones, not necessary use __try statements.



  • MFC exception

As for MFC exception, it does not belong to c++ exception, since when Microsoft introduces exception handling to MFC, the c++ exception standard may not be mature. In your new application, C++ exception is suggested, but the old MFC one is still available. But additional attention should be paid when mixing them in your application. Refer to msdn: http://msdn.microsoft.com/en-us/library/sas5wzs9%28v=vs.80%29.aspx. Since MFC macros automatically deleted caught exceptions when they go out of blocks, while c++ does not. So, don’t mix them in one block.


Furthermore, MFC exception is still a special SEH exception, whose code is: 0XE04D5343 and the ASCII is “.msc”, kind of microsoft standard.



  • DebugOutputString and exception

Another interesting thing is that DebugOutputString API is also implemented based on exception, so, in windbg we can also set breakpoints or execute commands based on the target’s debug output. For instance:


sxe out:Open?Database*: stop when finding matched trace output like: “Open Database 1”


.ocommand “MyWindbgCmd:” will execute command “!mk;g;” when finding “MyWindbgCmd: !mk;g;”



  • exception handler list in TEB

In the thread block associated with each thread, there is a pointer to stack of the exception handlers in the current thread. The top is the inner-most one.


The exception handler chain can be watched in Windbg via “!exchain”



  • some good article links:

Differences in Exception Handling Behavior Under /CLR


http://msdn.microsoft.com/en-us/library/2ww6y7y2.aspx


Exceptions: Converting from MFC Exception Macros


http://msdn.microsoft.com/en-us/library/19z28s5c.aspx

No comments:

Post a Comment