1. 程式人生 > >C++與SEH異常處理

C++與SEH異常處理

error C2712: Cannot use __try in functions that require object unwinding

原因是Windows的SEH異常處理程式無法處理C++的物件析構。

參考以下兩篇微軟的文章:

C++ exception handling builds on top of the OS SEH support. If you are writing C++ code, it is recommended you use C++ exception handing since SEH does not know how to handle C++ objects properly.

For example, for below code:

#include "stdafx.h"

 

class MyClass

{

public:

        ~MyClass()

        {

               printf("Myclass dtor\r\n");

        }

};

 

int _tmain(int argc, _TCHAR* argv[])

{

        __try

        {

               printf("in __try block\r\n");

              

               //create a auto object on the stack

               MyClass obj1;

 

               //attemp to write to memory address NULL to trigger AV

               int* p;

               p = 0;

               *p = 10;

 

        }

        __except(EXCEPTION_EXECUTE_HANDLER)

        {

               printf("in handler block\r\n");

        }

}

If we build the code with any of the three "Enable C++ Exceptions" options enabled (/EHa, /EHsc or /EHs, see http://msdn.microsoft.com/en-us/library/1deeycx5(VS.80).aspx), it fails with below error:



warning C4509: nonstandard extension used: 'wmain' uses SEH and 'obj1' has destructor

error C2712: Cannot use __try in functions that require object unwinding



If we set the "Enable C++ Exceptions" to no, the code compiles but destructor of obj1 does not get called.



If you want your C++ exception code to catch SEH exceptions, you need to build the code with /EHa. For example, for below code, if you build it with /EHa, the access violation can be catched and the destructor of obj1 will be called:
#include "stdafx.h"

 

class MyClass

{

public:

        ~MyClass()

        {

               printf("Myclass dtor\r\n");

        }

};

 

int _tmain(int argc, _TCHAR* argv[])

{

        try

        {

               printf("in __try block\r\n");

              

               //create a auto object on the stack

               MyClass obj1;

 

               //attemp to write to memory address NULL to trigger AV

               int* p;

               p = 0;

               *p = 10;

 

        }

        catch(...)

        {

               printf("in handler block\r\n");

        }

}

The destructor of obj1 is called during stack unwind:

               test.exe!MyClass::~MyClass

               msvcr100d.dll!_CallSettingFrame

               msvcr100d.dll!__FrameUnwindToStat

               msvcr100d.dll!CatchIt

               msvcr100d.dll!FindHandlerForForeignException

               msvcr100d.dll!FindHandler

               msvcr100d.dll!__InternalCxxFrameHandler

               msvcr100d.dll!__CxxFrameHandler3

               ntdll.dll!ExecuteHandler2

               ntdll.dll!ExecuteHandler

               ntdll.dll!_KiUserExceptionDispatcher

               test.exe!wmain

               test.exe!__tmainCRTStartup

               test.exe!wmainCRTStartup

               kernel32.dll!BaseThreadInitThunk

               ntdll.dll!__RtlUserThreadStart

               ntdll.dll!_RtlUserThreadStart

We can use _set_se_translator to "translate" SEH exceptions to C++ exceptions and handle them together in a consistent way, see http://msdn.microsoft.com/en-us/library/5z4bw5h5(VS.80).aspx

C++ exception does not support the EXCEPTION_CONTINUE_EXECUTION semantics, so if you want to catch the exception, handle it and then continue the execution from the instruction that causes the exceptions, you will need to mix C++ exception with SEH.

For /EHa, a means asynchronous since from the application's point of view, SEH exceptions (such as access violation, divide by zero, etc.) are asynchronous. C++ exceptions raised by throw statement are considered as synchronous.

From the OS perspective, although the OS handles interrupt and exceptions in a similar way (via the IDT table etc.), exceptions are considered synchronous, but interrupts are asynchronous.


MSDN上關於_set_se_translator 的示例程式碼:

// crt_set_se_translator_clr.cpp
// compile with: /clr
#include <windows.h>
#include <eh.h>
#include <assert.h>
#include <stdio.h>

int thrower_func(int i) {
   int j = i/0;
  return 0;
}

class CMyException{
};

#pragma unmanaged
void my_trans_func(unsigned int u, PEXCEPTION_POINTERS pExp )
{
printf("Translating the structured exception to a C++"
             " exception.\n");
throw CMyException();
}

void DoTest()
{
    try
    {
      thrower_func(10);
    } 

    catch(CMyException e)
    {
printf("Caught CMyException.\n");
    }
    catch(...)
    {
      printf("Caught unexpected SEH exception.\n");
    }
}
#pragma managed

int main(int argc, char** argv) {
    _set_se_translator(my_trans_func);
    DoTest();
    return 0;
}