此异常非彼异常(续2)

转载请注明出处

作者:小马

刚开始准备写关于异常的文章时,也没想到会一连写三篇.


本篇是关于如何把windows的SE异常与c++的try/catch整合在一起.


要实现整合, 要用_set_se_translator, 我并不是简单的直接用这个函数, 因为毕竟c++是一种面向对象语言, _set_se_translator是一个标准的c函数, 为了不破坏面向对象的思想, 我写了一类把_set_se_translator封装起来, 希望在实际的开发中, 可以类似这样应用(我把这个类的命名为CPony_SeException):


try 
{
 //各种处理......
} 

catch(CPony_SeException &e1)
{
 cout << "windows se exception happened" <<endl;
}
catch(runtime_error &e2) 
{
  cout << "c++ run-time error happened" <<endl;
}
catch(...)
{
 cout << "other c++ exception happened" <<endl;
}


 先简单说一下_set_se_translator的原理.

首先我们需要自己写一个函数, 这个函数的参数和返回值都必须符合下面的形式:

typedef void (*_se_translator_function)(unsigned int, struct _EXCEPTION_POINTERS* );

然后我们把这个函数作为参数, 传给_set_se_translator.这个过程可以认为是用_set_se_translator注册这个函数.


_set_se_translator是一个CRT,跟系统无关. 它提供了一种机制,可以把windows的seh异常转化为标准的c++异常(try/catch), 过程如下:当一个se异常产生时, 它会调用我们写的那个函数,而这个函数一般只做一个操作, 就是throw一个c++的异常.


另外, 补充两点, 


1 _set_se_translator是基于线程的, 对于多线程的应用,每一个线程都要转换一次.
2 用_set_se_translator必须打开/eha(前面文章介绍过如何在vs2005中打开这个开关)

我写的异常类来如下:

class CPony_SeException :public exception
{
public:
 CPony_SeException(CPony_SeException const& copee);
 CPony_SeException(unsigned int exceptionID, EXCEPTION_POINTERS* exceptionPointers);
 virtual ~CPony_SeException();

 unsigned int GetExceptionID() const;

 EXCEPTION_POINTERS* GetExceptionPointers() const;

 virtual char const * what() const;

private:
 
 static void sehTranslator(unsigned int exceptionID, EXCEPTION_POINTERS* exceptionPointers);

 unsigned int m_exceptionID;
 EXCEPTION_POINTERS* m_exceptionPointers;

 static _se_translator_function m_oldSEHTranslator;
};



比较简单的一个类, 解释几个地方:
1 exception是c++的异常基类, what是它的一个虚函数, c++中所有的异常类型都是派生自这个类, 更多详细的东西,可以看<<c++ primer>>.


2 EXCEPTION_POINTERS里记录了SE异常的详细内容, 比如异常发生的地址, 异常类型等, 我对外引出一个接口(GetExceptionPointers), 这样在异常发生时, 可以获取自己想要的信息.


3 GetExceptionID可以返回异常类型码, windows定义了以下几种:

#define WAIT_IO_COMPLETION                  STATUS_USER_APC
#define STILL_ACTIVE                        STATUS_PENDING
#define EXCEPTION_ACCESS_VIOLATION          STATUS_ACCESS_VIOLATION
#define EXCEPTION_DATATYPE_MISALIGNMENT     STATUS_DATATYPE_MISALIGNMENT
#define EXCEPTION_BREAKPOINT                STATUS_BREAKPOINT
#define EXCEPTION_SINGLE_STEP               STATUS_SINGLE_STEP
#define EXCEPTION_ARRAY_BOUNDS_EXCEEDED     STATUS_ARRAY_BOUNDS_EXCEEDED
#define EXCEPTION_FLT_DENORMAL_OPERAND      STATUS_FLOAT_DENORMAL_OPERAND
#define EXCEPTION_FLT_DIVIDE_BY_ZERO        STATUS_FLOAT_DIVIDE_BY_ZERO
#define EXCEPTION_FLT_INEXACT_RESULT        STATUS_FLOAT_INEXACT_RESULT
#define EXCEPTION_FLT_INVALID_OPERATION     STATUS_FLOAT_INVALID_OPERATION
#define EXCEPTION_FLT_OVERFLOW              STATUS_FLOAT_OVERFLOW
#define EXCEPTION_FLT_STACK_CHECK           STATUS_FLOAT_STACK_CHECK
#define EXCEPTION_FLT_UNDERFLOW             STATUS_FLOAT_UNDERFLOW
#define EXCEPTION_INT_DIVIDE_BY_ZERO        STATUS_INTEGER_DIVIDE_BY_ZERO
#define EXCEPTION_INT_OVERFLOW              STATUS_INTEGER_OVERFLOW
#define EXCEPTION_PRIV_INSTRUCTION          STATUS_PRIVILEGED_INSTRUCTION
#define EXCEPTION_IN_PAGE_ERROR             STATUS_IN_PAGE_ERROR
#define EXCEPTION_ILLEGAL_INSTRUCTION       STATUS_ILLEGAL_INSTRUCTION
#define EXCEPTION_NONCONTINUABLE_EXCEPTION  STATUS_NONCONTINUABLE_EXCEPTION
#define EXCEPTION_STACK_OVERFLOW            STATUS_STACK_OVERFLOW
#define EXCEPTION_INVALID_DISPOSITION       STATUS_INVALID_DISPOSITION
#define EXCEPTION_GUARD_PAGE                STATUS_GUARD_PAGE_VIOLATION
#define EXCEPTION_INVALID_HANDLE            STATUS_INVALID_HANDLE
#define EXCEPTION_POSSIBLE_DEADLOCK         STATUS_POSSIBLE_DEADLOCK


 

4 sehTranslator就是我们要自己实现的异常转换函数,注意这里把它定义成static,因为它是基于类的, 而不是某个对象.

5 m_oldSEHTranslator是一个函数指针.

 

下面到.cpp文件, 是类CPony_SeException的实现

 

#define CASE(nSeCode,szDescription) case nSeCode: \
 sprintf(szDescription, "%s", #nSeCode); \
 break;

这里定义一个宏, 把整型类型码转换成字符串, 下面的what函数会用到, 方便输出.

 

_se_translator_function CPony_SeException::m_oldSEHTranslator = _set_se_translator(CPony_SeException::sehTranslator);

_set_se_translator本身有个返回值, 是上一个被它注册的函数指针, 这里保存这个指针,以防会用到. 其实这里面还有一个重要的学问. 仔细看一下, 这一句我是在其它.cpp文件里调用的, sehTranslator是私有函数, 我用_set_se_translator直接访问, 为什么没有报错呢? 原因就是等号左边起了作用, <<c++ primer>>有说, 一旦成员名出现, static的定义就在类的作用域里了. 所以我可以访问它的私有成员. 可以试一下, 如果把=号左边的去掉,直接写:
_set_se_translator(CPony_SeException::sehTranslator);
程序会报错的.

 

CPony_SeException::CPony_SeException(CPony_SeException const& exceptionCopee)
:m_exceptionID(exceptionCopee.m_exceptionID), 
m_exceptionPointers(exceptionCopee.m_exceptionPointers)
{
}

CPony_SeException::CPony_SeException(unsigned int exceptionID, EXCEPTION_POINTERS* exceptionPointers)
: m_exceptionID(exceptionID), m_exceptionPointers(exceptionPointers)
{
}

CPony_SeException::~CPony_SeException()
{
}

unsigned int CPony_SeException::GetExceptionID() const
{
 return m_exceptionID;
}

EXCEPTION_POINTERS* CPony_SeException::GetExceptionPointers() const
{
 return m_exceptionPointers;
}

上面这些没什么好解释的.

 

char const * CPony_SeException::what() const
{
 char strError[100] = {0};
 static char strWhat[200] = {0};

 strcat(strWhat, "windows SE exception: ");

 switch (m_exceptionID)
 {
  CASE(EXCEPTION_ACCESS_VIOLATION,strError);
  CASE(EXCEPTION_DATATYPE_MISALIGNMENT,strError);
  CASE(EXCEPTION_BREAKPOINT,strError);
  CASE(EXCEPTION_SINGLE_STEP,strError);
  CASE(EXCEPTION_ARRAY_BOUNDS_EXCEEDED,strError);
  CASE(EXCEPTION_FLT_DENORMAL_OPERAND,strError);
  CASE(EXCEPTION_FLT_DIVIDE_BY_ZERO,strError);
  CASE(EXCEPTION_FLT_INEXACT_RESULT,strError);
  CASE(EXCEPTION_FLT_INVALID_OPERATION,strError);
  CASE(EXCEPTION_FLT_OVERFLOW,strError);
  CASE(EXCEPTION_FLT_STACK_CHECK,strError);
  CASE(EXCEPTION_FLT_UNDERFLOW,strError);
  CASE(EXCEPTION_INT_DIVIDE_BY_ZERO,strError);
  CASE(EXCEPTION_INT_OVERFLOW,strError);
  CASE(EXCEPTION_PRIV_INSTRUCTION,strError);
  CASE(EXCEPTION_IN_PAGE_ERROR,strError);
  CASE(EXCEPTION_ILLEGAL_INSTRUCTION,strError);
  CASE(EXCEPTION_NONCONTINUABLE_EXCEPTION,strError);
  CASE(EXCEPTION_STACK_OVERFLOW,strError);
  CASE(EXCEPTION_INVALID_DISPOSITION,strError);
  CASE(EXCEPTION_GUARD_PAGE,strError);
  CASE(EXCEPTION_INVALID_HANDLE,strError);
 default:
  strcpy(strError, "Unknown exception.");
  break;
 }
 strcat(strWhat, strError);
 return strWhat;
}

重定义的what函数, 用到了上面的宏, 这样可以以字符串的形式返回SE的异常类型码, 非常方便.

 

void CPony_SeException::sehTranslator(unsigned int exceptionID, 
         EXCEPTION_POINTERS* exceptionPointers)
{
 throw CPony_SeException(exceptionID, exceptionPointers);
}

这个就是异常转换函数, 它最好除了throw一个异常外, 什么也不要做.

 

好了, 类写完了, 我可以在我的main函数里这样用:

int main()
{
int *p = NULL;
try 
 {
  *p = 0;//非法内存访问
 } 
 //避免类对象的复制,这里用引用. 
 catch(CPony_SeException &e) 
 {
  cout << e.what() <<endl;
 } 
 return 0;
}


 

输出:
windows SE exception: EXCEPTION_ACCESS_VIOLATION

注意:一般要保证CPony_SeException类本身不能抛出SE异常, 如果出现了,肯定无法被它自己捕捉,也是会报系统错误的.


源码下载地址:

https://github.com/pony-maggie/LKE_lke2600_CardReader

 

你可能感兴趣的:(多线程,C++,exception,seh,Pointers)