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;
}