C++與Windows 32 結構化異常
C++的標準異常語句try catch只能捕獲c++自身的異常。也就是throw 語句丟擲的異常(也叫軟異常)
而專案中很多地方出錯的原因是由於空指標訪問,除0,或者堆疊溢位所導致的。(通常是Win32 系統異常,可能是軟異常也可能是贏異常)
這就需要有程式碼能捕獲這些異常。
而C++的try/catch是無法捕獲這種一異常的。
雖然有一個catch(...)在設定了編譯屬性以後可以捕獲SEH,但是可以提供的有價值的資訊幾乎為0.
然後windows系統預設的SEH機制,通常不能用於有解構函式的程式碼。也就是說如果程式碼中使用了C++的類,存在解構函式
__try/__catch就會導致不能執行unwind而編譯錯誤,錯誤資訊通常如下:
error C2712: Cannot use __try in functions that require object unwinding
那有沒有辦法在c++程式碼中捕獲這種異常呢?
據說使用GX連結開關可以。我沒研究出來。
在網上到一篇整合了Win32結構並在C++程式碼中使用的文章寫的非常好
http://blog.csdn.net/chenyu2202863/article/details/3260345
這裡講解一下:
一,首先需要設定一個轉義函式,能夠將SEH解釋成c++的異常。
例如一下程式碼:
_set_se_translator(seh_translator);
設定一個回撥函式seh_translator為轉義函式。
二,轉義函式,對常見的Win32異常進行了封裝,將異常程式碼轉義成特定的異常類並丟擲。
// global structured exception handler translator call back function. static void seh_translator(unsigned code, EXCEPTION_POINTERS * info) { switch (code) { case EXCEPTION_ACCESS_VIOLATION: throw access_violation(*info); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_DIVIDE_BY_ZERO: throw divide_by_zero(*info); break; case EXCEPTION_STACK_OVERFLOW: throw stack_overflow(*info); default: throw structured_exception(*info); break; } }
三,這樣C/C++的Try/Catch語句就可以捕獲到這樣的異常。
int main()
{
structured_exception::install();
//
// discriminate exception by dynamic type
//
try
{
*(int *) 0 = 0; // generate Structured Exception
}
catch (structured_exception const &exception)
{
cout << "caught " << exception.what() << endl;
}
//
// discriminate exception by static type
//
try
{
static volatile int i = 0;
i = 1 / i; // generate Structured Exception
}
catch (access_violation const &)
{
cout << "caught access violation" << endl;
}
catch (divide_by_zero const &)
{
cout << "caught divide by zero" << endl;
}
catch (structured_exception const &)
{
cout << "caught unspecified Structured Exception" << endl;
}
return 0;
}
四,專案配置
要啟用SEH轉換成C++ Exception的功能也就是讓__set_seh_translator有效,需要開啟如下編譯器選項。
該函式是針對執行緒有效的,因此要啟用該函式需要對每個 執行緒都進行一次設定。
該函式是針對模組有效的(也就是native code)。如果你的專案有多個DLL,只進行一次設定是無效的。必須將相關的程式碼編譯成native code。(等待測試)
一個測試程式碼:
Exception.h
#ifndef XAP_WIN32SEH_H
#define XAP_WIN32SEH_H
#include <ut_export.h>
#include <windows.h>
#include <exception>
//////////////////////////////////////////////////////////////////////////
class ABI_EXPORT structured_exception : public std::exception
{
public:
structured_exception(EXCEPTION_POINTERS const &) throw();
static void install() throw();
virtual const char* what() const throw();
const void * address() const throw();
unsigned getExceptionCode() const throw();
private:
const void *m_ExceptionAddress;
unsigned m_Exceptioncode;
};
//////////////////////////////////////////////////////////////////////////
// handle for EXCEPTION_ACCESS_VIOLATION
class ABI_EXPORT access_violation : public structured_exception
{
public:
access_violation(EXCEPTION_POINTERS const&) throw();
virtual const char* what() const throw();
};
// handle for EXCEPTION_INT_DIVIDE_BY_ZERO
class ABI_EXPORT divide_by_zero : public structured_exception
{
public:
divide_by_zero(EXCEPTION_POINTERS const &) throw();
virtual const char* what() const throw();
};
// handle for EXCEPTION_STACK_OVERFLOW
class ABI_EXPORT stack_overflow : public structured_exception
{
public:
stack_overflow(EXCEPTION_POINTERS const &) throw();
virtual const char* what() const throw();
};
#endif
Exception.cpp
#include "xap_Win32SEH.h"
#include <eh.h> // for _set_se_translator
// global structured exception handler translator call back function.
static void seh_translator(unsigned code, EXCEPTION_POINTERS * info)
{
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
throw access_violation(*info);
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
throw divide_by_zero(*info);
break;
case EXCEPTION_STACK_OVERFLOW:
throw stack_overflow(*info);
default:
// we only throw the structured exception which is known.
//throw structured_exception(*info);
break;
}
}
structured_exception::structured_exception(EXCEPTION_POINTERS const & info) throw()
{
EXCEPTION_RECORD const &exception = *(info.ExceptionRecord);
m_ExceptionAddress = exception.ExceptionAddress;
m_Exceptioncode = exception.ExceptionCode;
}
// install the global structured exception handler translator.
void structured_exception::install() throw()
{
_set_se_translator(seh_translator);
}
// return exception type string
const char * structured_exception::what() const throw()
{
return "unspecified structured exception";
}
// return exception address.
const void * structured_exception::address() const throw()
{
return m_ExceptionAddress;
}
// return exception code.
unsigned structured_exception::getExceptionCode() const throw()
{
return m_Exceptioncode;
}
//////////////////////////////////////////////////////////////////////////
// Implementation for Win32 SEH class
//
// access_violation:
//
access_violation::access_violation(EXCEPTION_POINTERS const& info) throw()
: structured_exception(info)
{
}
const char* access_violation::what() const throw()
{
return "access violation";
}
//
// divide_by_zero:
//
divide_by_zero::divide_by_zero(EXCEPTION_POINTERS const & info) throw()
: structured_exception(info)
{
}
const char* divide_by_zero::what() const throw()
{
return "divide by zero";
}
//
// stack_overflow
//
stack_overflow::stack_overflow(EXCEPTION_POINTERS const & info) throw()
: structured_exception(info)
{
}
const char* stack_overflow::what() const throw()
{
return "stack overflow";
}