异常处理

来源:互联网 发布:excel数据分析怎么做 编辑:程序博客网 时间:2024/06/05 16:00

下面的代码举例说明了一种方法,指出所发生异常的类别:
BOOL Func_SEHExceptionGetCodeBase()
{
int x,y;
__try{
  x = 0;
  y = 4/x;
}
__except((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ? /
                                EXCEPTION_EXECUTE_HANDLER :/
                EXCEPTION_CONTINUE_SEARCH)
{
  file://handle divide by zero exception
}
}
GetExceptionCode返回一个值,该值指出所发生异常的种类:
下面列出所有预定义的异常和相应的含意,这些内容取自Platform SDK文档。这些异常标识符可以在WinBase.h文件中找到。我们对这些异常做了分类。
1. 与内存有关的异常
• E X C E P T I O N _ A C C E S S _ V I O L AT I O N。线程试图对一个虚地址进行读或写,但没有做适当的存取。这是最常见的异常。
• E X C E P T I O N _ D ATAT Y P E _ M I S A L I G N M E N T。线程试图读或写不支持对齐( a l i g n m e n t)的硬件上的未对齐的数据。例如, 1 6位数值必须对齐在2字节边界上,3 2位数值要对齐在4字节边界上。
• E X C E P T I O N _ A R R AY _ B O U N D S _ E X C E E D E D。线程试图存取一个越界的数组元素,相应的硬件支持边界检查。
• E X C E P T I O N _ I N _ PA G E _ E R R O R。由于文件系统或一个设备启动程序返回一个读错误,造成不能满足要求的页故障。
• E X C E P T I O N _ G U A R D _ PA G E。一个线程试图存取一个带有PA G E _ G U A R D保护属性的内存页。该页是可存取的,并引起一个E X C E P T I O N _ G U A R D _ PA G E异常。
• EXCEPTION_STA C K _ O V E R F L O W。线程用完了分配给它的所有栈空间。
• E X C E P T I O N _ I L L E G A L _ I N S T R U C T I O N。线程执行了一个无效的指令。这个异常由特定的C P U结构来定义;在不同的C P U上,执行一个无效指令可引起一个陷井错误。
• E X C E P T I O N _ P R I V _ I N S T R U C T I O N。线程执行一个指令,其操作在当前机器模式中不允许。
2. 与异常相关的异常
• E X C E P T I O N _ I N VA L I D _ D I S P O S I T I O N。一个异常过滤器返回一值,这个值不是E X C E P T I O N _ E X E C U T E _ H A N D L E R 、E X C E P T I O N _ C O N T I N U E _ S E A R C H、E X C E P T I O N _ C O N T I N U E _ E X E C U T I O N三者之一。
• E X C E P T I O N _ N O N C O N T I N U A B L E _ E X C E P T I O N。一个异常过滤器对一个不能继续的异常返回E X C E P T I O N _ C O N T I N U E _ E X E C U T I O N。
3. 与调试有关的异常
• EXCEPTION_BREAKPOINT。遇到一个断点。
• E X C E P T I O N _ S I N G L E _ S T E P。一个跟踪陷井或其他单步指令机制告知一个指令已执行完毕。
• E X C E P T I O N _ I N VA L I D _ H A N D L E。向一个函数传递了一个无效句柄。
4. 与整数有关的异常
• EXCEPTION_INT_DIVIDE_BY_ZERO。线程试图用整数0来除一个整数
• EXCEPTION_INT_OVERFLOW。一个整数操作的结果超过了整数值规定的范围。
5. 与浮点数有关的异常
• E X C E P T I O N _ F LT _ D E N O R M A L _ O P E R A N D。浮点操作中的一个操作数不正常。不正常的值是一个太小的值,不能表示标准的浮点值。
• EXCEPTION_FLT _ D I V I D E _ B Y _ Z E R O。线程试图用浮点数0来除一个浮点。
• EXCEPTION_FLT _ I N E X A C T _ R E S U LT。浮点操作的结果不能精确表示成十进制小数。
• EXCEPTION_FLT _ I N VA L I D _ O P E R AT I O N。表示任何没有在此列出的其他浮点数异常。
• EXCEPTION_FLT _ O V E R F L O W。浮点操作的结果超过了允许的值。
• EXCEPTION_FLT _ S TA C K _ C H E C K。由于浮点操作造成栈溢出或下溢。
• EXCEPTION_FLT _ U N D E R F L O W。浮点操作的结果小于允许的值。
函数GetExceptionCode只能在一个过滤器中调用(__except之后的括号里),或在一个异常处理程序中被调用。记住,不能在一个异常过滤器函数里面调用GetExceptionCode。编译程序会捕捉这样的错误。

还有,上面的异常代码遵循在WinError.h文件中定义的有关错误代码的规则。(捎后我会写一段小的文章表明WinError.h的作用)

GetExceptionInformation
当一个异常发生时,操作系统要向引起异常的线程的栈里压入三个结构,这三个结构是:EXCEPTION_RECORD结构、CONTEXT结构和EXCEPTION_POINTERS结构。
EXCEPTION_RECORD结构包含有关已发生异常的独立于C P U的信息,
CONTEXT结构包含已发生异常的依赖于C P U的信息。
EXCEPTION_POINTERS结构只有两个数据成员,二者都是指针,分别指向被压入栈的EXCEPTION_POINTERS和CONTEXT结构。

为了取得这些信息并在你自己的程序中使用这些信息,需要调用GetExceptionInformation
函数:这个内部函数返回一个指向EXCEPTION_POINTERS结构的指针。

关于GetExceptionInformation函数,要记住的最重要事情是它只能在异常过滤器中调用,因为仅仅在处理异常过滤器时, EXCEPTION_RECORD和EXCEPTION_POINTERS才是有效的。一旦控制被转移到异常处理程序,栈中的数据就被删除。

如果需要在你的异常处理程序块里面存取这些异常信息(虽然很少有必要这样做),必须将EXCEPTION_POINTERS结构所指向的CONTEXT数据结构和/或EXCEPTION_RECORD数据结构保存在你所建立的一个或多个变量里。下面的代码说明了如何保存EXCEPTION_RECORD和CONTEXT数据结构:
void Func_SEHExceptionGetInfo()
{
EXCEPTION_RECORD SaveExceptRec;
CONTEXT SaveExceptContext;
int x = 0;

__try{
  x = 5/x;
}
__except(SaveExceptRec =
        *(GetExceptionInformation())->ExceptionRecord,
     SaveExceptContext =
     *(GetExceptionInformation())->ContextRecord,
     EXCEPTION_EXECUTE_HANDLER
     )
{
  switch(SaveExceptRec.ExceptionCode)
  {
   file://Here, Do your thing for code value
  }

}
}
上面的代码中:注意在异常过滤器中C语言逗号(,)操作符的使用。许多程序员不习惯使用这个操作符。它告诉编译程序从左到右执行以逗号分隔的各表达式。当所有的表达式被求值之后,返回最后的(或最右的)表达式的结果。

下面是EXCEPTION_RECORD的结构:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
EXCEPTION_RECORD结构包含有关最近发生的异常的详细信息,这些信息独立于C P U:
• ExceptionCode包含异常的代码。这同内部函数GetExceptionCode返回的信息是一样的。

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/njuitjf/archive/2009/03/27/4029964.aspx

 

Windows提供了两个API函数来获取异常信息:

LPEXCEPTION_POINTERS GetExceptionInformation(VOID); //取得异常相关信息

DWORD GetExceptionCode(VOID); // 取得异常编号

GetExceptionCode()返回异常编号,而GetExceptionInformation()返回更丰富的信息,EXCEPTION_POINTERS结构如下,

typedef struct _EXCEPTION_POINTERS { // exp

PEXCEPTION_RECORD ExceptionRecord;

PCONTEXT ContextRecord;

} EXCEPTION_POINTERS;

其中EXCEPTION_RECORD类型,它记录了一些与异常相关的信息;而CONTEXT数据结构体中记录了异常发生时,线程当时的上下文环境,主要包括寄存器的值。

有了这些信息,__except模块便可以对异常错误进行很好的分类和恢复处理,通常我们需要一个过滤函数来辅助。一般称为是filterfunction.过滤函数只过滤需要处

理的异常。

int exception_access_violation_filter(LPEXCEPTION_POINTERS p_exinfo)

{

   if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)

   {

   messagebox("access vialation exceptionn");

   return EXCEPTION_EXECUTE_HANDLER ; //告诉except处理这个异常

   }

   else return EXCEPTION_CONTINUE_SEARCH; //不告诉except处理这个异常

}

int exception_int_divide_by_zero_filter(LPEXCEPTION_POINTERS p_exinfo)

{

   if(p_exinfo->ExceptionRecord->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO)

   {

   return EXCEPTION_EXECUTE_HANDLER; //告诉except处理这个异常

   }

   else return EXCEPTION_CONTINUE_SEARCH; //不告诉except处理这个异常

}

于是,你可以这样写这段异常处理代码:

__try

{

   // guarded code

}

__except(exception_access_violation_filter(GetExceptionInformation()))

{

//

}

__try

{

   // guarded code

}

__exceptexception_int_divide_by_zero_filter(GetExceptionInformation()))

{

//exception handling

}

SEH异常处理模型中,也可以抛出一个异常。对应的WindowsAPI函数是RaiseException,

VOID RaiseException(

DWORD dwExceptionCode, // 异常的编号

DWORD dwExceptionFlags, // 异常标记

DWORD nNumberOfArguments, // 参数个数

CONST DWORD *lpArguments // 参数数组首地址

);

通常,后三个参数基本不用

SEH异常处理还有try-finally.类似于java里的try-catch-finally.但是SEH的try只能和except和finally两者之间的一个搭配,不能有try-except-finnaly.

C++异常模型用try-catch语法定义,而SEH异常模型则用try-except语法,与C++异常模型相似,try-except也支持多层的try-except嵌套。

try-except模型中,一个try块只能是有一个except块;而C++异常模型中,一个try块可以有多个catch块。

C++异常模型是按照异常对象的类型来进行匹配查找的;而try-except模型则不同,它通过一个表达式的值来进行判断.

__except关键字后面跟的表达式,它可以是各种类型的表达式,例如,它可以是一个函数调用,或是一个条件表达式,或是一个逗号表达式,或干脆就是一个整

型常量等等。最常用的是一个函数表达式,并且通过利用GetExceptionCode()或GetExceptionInformation ()函数来获取当前的异常错误信息,便于程序员有效控制异常

错误的分类处理。

SEH异常处理模型中,异常通过RaiseException()函数抛出。RaiseException()函数的作用类似于C++异常模型中的throw。

关于SEH异常处理更详细的资料,你可以去看windows via c/c++这本书,中文译名是windows核心编程。不过还是建议你看英文原版,翻译的版本质量不高。