o 操作系统产生的SHE
o C运行库产生CRT错误
o 信号
每一种异常都需要安装exception handler函数来处理这些异常,以此来防止程序由于这些异常而crash,或者在程序因异常而退出时留下遗言,主要是打印出当时的栈信息,方便我们对其进行分析。
如果应用程序是多线程,那么情况变得复杂了。因为有的handler工作在整个进程,而有的工作只是工作在当前线程,此时需要为每一个线程安装handler。
而异常产生的原因如下:
o 程序访问非法内存地址(如NULL指针)
o 栈溢出
o 大块数据写入到小的buffer内
o 纯虚函数被调用
o 内存不能够被allocate
o 非法参数传入C++系统函数
o C运行库检测到错误,要求程序退出
SEH handling
C语言处理SEH异常,常用__try{} __except(){}.
C++语言处理SHE异常,用_set_se_traslator()把这个异常转化为C++异常。注意:这个函数工作于每一个线程中。
MSDN 例子
// crt_settrans.cpp
// compile with: /EHa
#include <stdio.h>
#include <windows.h>
#include <eh.h>
void SEFunc();
void trans_func( unsigned int, EXCEPTION_POINTERS* );
class SE_Exception
{
private:
unsigned int nSE;
public:
SE_Exception() {}
SE_Exception( unsigned int n ) : nSE( n ) {}
~SE_Exception() {}
unsigned int getSeNumber() { return nSE; }
};
int main( void )
{
try
{
_set_se_translator( trans_func );
SEFunc();
}
catch( SE_Exception e )
{
printf( "Caught a __try exception with SE_Exception.\n" );
}
}
void SEFunc()
{
__try
{
int x, y=0;
x = 5 / y;
}
__finally
{
printf( "In finally\n" );
}
}
void trans_func( unsigned int u, EXCEPTION_POINTERS* pExp )
{
printf( "In trans_func.\n" );
throw SE_Exception();
__try{}__catch(Expression){}缺点在于,用户可能会忘记保证潜在错误code引起的异常,而这个异常不能够被程序捕获。这样的SHE异常需要SetUnhandleExceptionFilter()函数来处理。该函数工作于整个进程。MSDN 例子
LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionPtrs)
{
// Do something, for example generate error report
//..
// Execute default exception handler next
return EXCEPTION_EXECUTE_HANDLER;
}
void main()
{
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
// .. some unsafe code here
Note: if someone calls the SetUnhandledExceptionFilter() function after your call, the exception filter will be replaced. This is a disadvantage because you can't chain top-level handlers one above another. If your exception handler function is located inside of a DLL, you should be careful when using the SetUnhandledExceptionFilter() function. If your DLL is unloaded at the moment of crash, the behavior may be unpredictable.
Vectored Exception Handling
VEH作为SHE异常的扩展,由windows XP引入。调用AddVectorExceptionHandler()来添加异常处理函数。
Terminate Handler
当CRT遇到未处理C++类型异常时,会调用terminate()。为了阻止这样调用,可以设置错误处理函数set_terminate()。同样对于unexpected()函数,可以通过set_unexpected()处理该函数。这两个函数均作用于每一个线程。MSDN 例子
void my_terminate_handler()
{
// Abnormal program termination (terminate() function was called)
// Do something here
// Finally, terminate program
exit(1);
}
void main()
{
set_terminate(my_terminate_handler);
terminate();
Pure Call Handler
使用_set_purecall_handler()函数来处理纯虚函数调用。该函数工作于程序整个进程中。MSDN 例子
// _set_purecall_handler.cpp
// compile with: /W1
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
class CDerived;
class CBase
{
public:
CBase(CDerived *derived): m_pDerived(derived) {};
~CBase();
virtual void function(void) = 0;
CDerived * m_pDerived;
};
class CDerived : public CBase
{
public:
CDerived() : CBase(this) {}; // C4355
virtual void function(void) {};
};
CBase::~CBase()
{
m_pDerived -> function();
}
void myPurecallHandler(void)
{
printf("In _purecall_handler.");
exit(0);
}
int _tmain(int argc, _TCHAR* argv[])
{
_set_purecall_handler(myPurecallHandler);
CDerived myDerived;
New Operator Fault Handler
使用_set_new_handler()来处理内存分配错误。该函数作用于程序整个进程中。同时采用_set_new_handler()定义对于malloc的错误处理。MSDN 例子
#include <new.h>
int handle_program_memory_depletion( size_t )
{
// Your code
}
int main( void )
{
_set_new_handler( handle_program_memory_depletion );
int *pi = new int[BIG_NUMBER];
Invalid Parameter handler
使用_set_invalid_parameter_handler()函数来处理CRT检测到系统调用中错误参数的case。该函数工作于整个进程中。适用于VS2005之后。MSDN 例子
// crt_set_invalid_parameter_handler.c
// compile with: /Zi /MTd
#include <stdio.h>
#include <stdlib.h>
#include <crtdbg.h> // For _CrtSetReportMode
void myInvalidParameterHandler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
wprintf(L"Invalid parameter detected in function %s."
L" File: %s Line: %d\n", function, file, line);
wprintf(L"Expression: %s\n", expression);
}
int main( )
{
char* formatString;
_invalid_parameter_handler oldHandler, newHandler;
newHandler = myInvalidParameterHandler;
oldHandler = _set_invalid_parameter_handler(newHandler);
// Disable the message box for assertions.
_CrtSetReportMode(_CRT_ASSERT, 0);
// Call printf_s with invalid parameters.
formatString = NULL;
printf(formatString);
Singnal handling
信号是C++提供的程序中断机制,函数signal来处理这些信号。
在VS中,有如下的信号:
- SIGABRT Abnormal termination
- SIGFPE Floating-point error
- SIGILL Illegal instruction
- SIGINT CTRL+C signal
- SIGSEGV Illegal storage access
- SIGTERM Termination request
MSDN说SIGILL,SIGSEGV和SIGTERM,不产生在windows下。但是,实践发现SetUnhandledExceptionFilter()函数代替了SIGSEGV。而_pxcptinfoptrs用来处理SIGFPE。MSDN 例子
void sigabrt_handler(int)
{
// Caught SIGABRT C++ signal
// Terminate program
exit(1);
}
void main()
{
signal(SIGABRT, sigabrt_handler);
// Cause abort
abort();
综上所述,我们给出如下handling函数,来自http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus
void CCrashHandler::SetProcessExceptionHandlers()
{
// Install top-level SEH handler
SetUnhandledExceptionFilter(SehHandler);
// Catch pure virtual function calls.
// Because there is one _purecall_handler for the whole process,
// calling this function immediately impacts all threads. The last
// caller on any thread sets the handler.
// http://msdn.microsoft.com/en-us/library/t296ys27.aspx
_set_purecall_handler(PureCallHandler);
// Catch new operator memory allocation exceptions
_set_new_handler(NewHandler);
// Catch invalid parameter exceptions.
_set_invalid_parameter_handler(InvalidParameterHandler);
// Set up C++ signal handlers
_set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);
// Catch an abnormal program termination
signal(SIGABRT, SigabrtHandler);
// Catch illegal instruction handler
signal(SIGINT, SigintHandler);
// Catch a termination request
signal(SIGTERM, SigtermHandler);
}
void CCrashHandler::SetThreadExceptionHandlers()
{
// Catch terminate() calls.
// In a multithreaded environment, terminate functions are maintained
// separately for each thread. Each new thread needs to install its own
// terminate function. Thus, each thread is in charge of its own termination handling.
// http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx
set_terminate(TerminateHandler);
// Catch unexpected() calls.
// In a multithreaded environment, unexpected functions are maintained
// separately for each thread. Each new thread needs to install its own
// unexpected function. Thus, each thread is in charge of its own unexpected handling.
// http://msdn.microsoft.com/en-us/library/h46t5b69.aspx
set_unexpected(UnexpectedHandler);
// Catch a floating point error
typedef void (*sigh)(int);
signal(SIGFPE, (sigh)SigfpeHandler);
// Catch an illegal instruction
signal(SIGILL, SigillHandler);
// Catch illegal storage access errors
signal(SIGSEGV, SigsegvHandler);