(structured exception handing)SEH 包含终止处理和异常处理。本章讨论异常处理。
 
当一个硬件或者软件异常被抛出的时候,操作系统会给我们程序一个查看异常类型的机会,并允许程序自己处理这个异常。下面演示了异常处理程序的语法结构:
 
_try{
 // Guarded body
 }
_except(EXCEPTION_EXECUTE_HANDLER) // 也可以是EXCEPTION_CONTINUE_SEARCH或者EXCEPTION_CONTINUE_EXECUTE
{
  // Exception handler
}
 
请注意:try 语句后必须但只能跟一个 finally 或者 except 块。不过可以将 try-finally 嵌套于 try-except 中。
 

 

异常过滤程序的返回值:

EXCEPTION_EXECUTE_HANDLER(1):执行except块,执行完except块以后,接着执行except块之后的代码,而不会返回到出现异常的代码
EXCEPTION_CONTINUE_SEARCH(0):搜索更高层的except进行执行
EXCEPTION_CONTINUE_EXECUTION(-1):系统将程序控制流跳转到导致异常那条指令,尝试重新执行
 

 

EXCEPTION_EXECUTE_HANDLER 之 全局展开

 
我们知道,一个线程离开 try-finally 结果的 try 块后,系统需要确保 finally 块的代码得到执行。发生异常时,系统用来确保这条规则成立的机制就是全局展开。
 
当异常过滤程序的计算结果为 EXCEPTION_EXECUTE_HANDLER 时,系统必须执行全局展开。全局展开导致所有已经开始执行但尚未完成的 try-finally 块得以继续执行,在调用栈中,这些 try-finally 块位于对异常进行了处理的 try-except 块的下方。
 
 
实例程序:
 
#include<iostream>
#include<Windows.h>
using namespace std; DWORD g_dwProtectData = 10; void FuncRen1()
{
DWORD dwTemp = 0; __try
{
__try
{
g_dwProtectData = 5 / dwTemp; // 除0,产生异常
}
__finally // 已经开始执行但未完成的带 finally 的 try 块
{
MessageBox(NULL, "This is finally", 0, NULL);
} g_dwProtectData = 5 / 2; // 调试,不会被执行
}
__finally // 已经开始执行但未完成的带 finally 的 try 块
{
MessageBox(NULL, "This is finally 2", 0, NULL);
} __try
{
g_dwProtectData = 5 / 3; // 除0,产生异常
}
__finally // 未开始执行但未完成的带 finally 的 try 块
{
MessageBox(NULL, "This is finally 3", 0, NULL); // 不会执行
}
} int main()
{
__try
{
FuncRen1();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
MessageBox(NULL, "This is except", 0, NULL);
}
}
 
 
过程:
 
注:这里向上是指先被执行的代码。
 
FuncRen1 中0作为除数的运算产生一个异常,系统夺回控制权,搜索与 try 匹配的 except 块。因为在 FuncRen1 中与 try 匹配的是 finally,于是系统在调用栈中向上搜索之前的 try 块,它会看到函数 main 中 try 与 except 匹配。
 
于是系统执行与 main 中 except 相关的异常过虑程序,得到 EXCEPTION_EXECUTE_HANDLER 结果,马上在 FuncRen1 的 finally 块中全局展开,此时 main 中 except 块还未执行。在进行全局展开的时候,系统会从调用栈的底部向上搜索所有已经开始执行但尚为完成的 try 块,寻找其中带有 finally 的 try 块,然后会执行这些 finally 块中内容。
 
最后 finally 中代码执行完之后,系统继续在调用栈中向上寻找需要被执行的 finally,如果没有,就执行 except 代码块。
 

 

停止全局展开

 
可以通过将 return 语句置于 finally 块中以阻止系统完成全局展开。这会导致调用 return 的函数结束,控制流回到上一个函数,并让系统正常执行,就好像没有发生过一样,except 代码块也不会执行。
 
#include<iostream>
#include<Windows.h>
using namespace std; DWORD g_dwProtectData = 10; void FuncRen1()
{
DWORD dwTemp = 0; __try
{
g_dwProtectData = 5 / dwTemp; // 除0,产生异常
}
__finally // 已经开始执行但未完成的带 finally 的 try 块
{
return; // 停止全局展开,函数停止,控制权流回到上一个函数,
// 并让系统正常执行,就好像没有发生过异常一样
}
} void Func1()
{
FuncRen1();
MessageBox(NULL, "Func1", "Warning", NULL); // 因为停止全局展开,控制权回到这里,会执行
} int main()
{
__try
{
Func1();
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
MessageBox(NULL, "This is except", "Warning", NULL); // 不会执行
// 停止全局展开...让系统正常执行,就好像没有发生过异常一样
}
}
 
结果:
 
 

 

EXCEPTION_CONTINUE_SEARCH(0):搜索更高层的except进行执行

 
实例程序:
 
#include<iostream>
#include<Windows.h>
#include <tchar.h>
using namespace std; TCHAR g_szBuffer[100]; LONG OliFilter3(TCHAR **ppchBuffer)
{
if (*ppchBuffer == NULL)
{
*ppchBuffer = g_szBuffer; // ④修复之前pchBuffer = NULL的错误
return EXCEPTION_CONTINUE_EXECUTION; // ⑤继续执行异常位置
}
return EXCEPTION_EXECUTE_HANDLER; // ⑦第二次执行这个函数时,pchBuffer != NULL,此处被执行
} void FuncAtude3(TCHAR *sz)
{
__try {
*sz = TEXT('\0'); // ① 未初始化指针,异常
// ⑥ 局部变量 sz 还是NULL,依然异常,再执行一次①~③
}
__except (EXCEPTION_CONTINUE_SEARCH) { // ②让系统在调用栈中向上找前一个带except的try代码块,并调用其异常过虑程序
// 这里永远不会被执行
}
} void FunclinRoosevelt3()
{
TCHAR *pchBuffer = NULL; __try
{
FuncAtude3(pchBuffer); MessageBox(NULL, "This is FunclinRoosevelt3 before", "Warning", NULL);
}
__except(OliFilter3(&pchBuffer)) // ③由于 EXCEPTION_CONTINUE_SEARCH,这里被调用。
{
MessageBox(NULL, "This is FunclinRoosevelt3", "Warning", NULL); // ⑧ 执行
}
} int main()
{
FunclinRoosevelt3();
}
 
结果:
 

 

GetExceptionCode

 
GetExceptionCode 是内在函数,其返回值表明刚发生过的异常的类型。
 
  DWORD GetExceptionCode();
 
返回值来自于 Platform SDK 的文档,可以在 WinBase.h 中找到它们:
 
// 内存相关异常
EXCEPTION_ACCESS_VIOLATION 写一个错误的虚拟地址
EXCEPTION_DATATYPE_MISALIGNMENT 线程试图从没有提供自动对齐机制的硬件里读入没有对齐的数据。
EXCEPTION_ARRAY_BOUNDS_EXCEEDED 线程在支持边界检查的硬件上访问越界的数组元素。
EXCEPTION_IN_PAGE_ERROR 文件系统或设备取得读取错误而引起的页错误。
EXCEPTION_GUARD_PAGE 线程试图访问具备PAGE_GUARD属性内存页。
EXCEPTION_STACK_OVERFLOW 线程用光栈空间
EXCECPTION_ILLEGAL_INSTRUCTION 非法指令
EXCEPTION_PRIV_INSTRUCTION 试图执行当前机器模式下不允许的操作
 
// 与异常本身相关的异常
EXCEPTION_INVALID_DISPOSITION 异常过滤返回3个常量以外值
EXCEPTION_NONCONTINUABLE_EXCEPTION 异常过滤返回继续执行,但实际这类异常不能继续
 
// 与调试相关异常
EXCEPTION_BREAKPOINT 执行到断点
EXCEPTION_SINGLE_STEP 单步
EXCEPTION_INVALID_HANDLE 传入无效句柄给一函数
 
// 与整型相关的异常
EXCEPTION_INT_DIVIDE_BY_ZERO 线程试图在整数除法运算中以0做除数
EXCEPTION_INT_OVERFLOW 整型运算结果超出范围
 
// 与浮点类型相关的异常
EXCEPTION_FLT_DENORMAL_OPERAND 浮点运算中一个运算数是不能作为标准浮点数的小数。
EXCEPTION_FLT_DIVIDE_BY_ZERO 线程试图在浮点除法中以0做除数
EXCEPTION_FLT_INEXACT_RESULT 浮点运算结果不能精确地表示为十进制小数
EXCEPTION_FLT_INVALID_OPERATION 其它浮点数异常
EXCEPTION_FLT_OVERFLOW 浮点运算指数部分超出该类型允许的最大值
EXCEPTION_FLT_STACK_CHECK 浮点运算造成栈上溢出或下溢出
EXCEPTION_FLT_UNDERFLOW 浮点运算指数部分小于该类型允许的最小值
 
#include<iostream>
#include<Windows.h>
#include <tchar.h>
using namespace std; void FunclinRoosevelt3()
{
__try {
int y = 0;
int x = 4 / y;
}
__except (
((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ||
(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO)) ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { switch (GetExceptionCode())
{
case EXCEPTION_ACCESS_VIOLATION:
MessageBox(NULL, "EXCEPTION_ACCESS_VIOLATION", 0, NULL);
break; case EXCEPTION_INT_DIVIDE_BY_ZERO:
MessageBox(NULL, "EXCEPTION_INT_DIVIDE_BY_ZERO", 0, NULL); // 执行
break;
}
}
} int main()
{
FunclinRoosevelt3();
}
 
但是我们不能在异常过虑程序中调用 GetExceptionCode ,所以以下代码会编译报错。
  
 
但是可以这样写:将 GetExceptionCode 返回值作为参数传入异常过虑程序。
  __except (CoffeeFilter(GetExceptionCode()))
 

 

GetExceptionInformation

 
一个异常发生时,系统将向发生异常的线程的栈中压入3个结构,
  • EXCEPTION_RECORD// 包含关于抛出异常的信息
  • CONTEXT// 与异常和CPU相关的信息
  • EXCEPTION_POINTERS// 含两个指针,指向前两个结构
 
这个函数只能在异常过滤程序中调用,因为上述3个结构只在系统计算异常过滤程序时才有效。
仅仅限与_except后的括号里。
 
如果要得到这些信息,并在程序中使用,可以调用 GetExceptionInformation 函数
 
  PEXCEPTION_POINTERS GetExceptionInformation();
 
// 包含关于抛出异常的信息
typedef struct _EXCEPTION_RECORD
{
// 异常码,就是 GetExceptionCode 的返回值
DWORD ExceptionCode;
// 异常标志
DWORD ExceptionFlags;
// 异常链
struct _EXCEPTION_RECORD *ExceptionRecord;
// 导致异常的CPU指令地址
PVOID ExceptionAddress;
// 异常相关参数个数
DWORD NumberParameters;
// 描述异常的附加数组
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
}EXCEPTION_RECORD;
 
 
EXCEPTION_RECORD SavedExceptRec;	// 包含关于抛出异常的信息
CONTEXT SacedContext; // 与异常和CPU相关的信息 __try {
// ...
}
__except (
SavedExceptRec = *(GetExceptionInformation())->ExceptionRecord,
SacedContext = *(GetExceptionInformation())->ContextRecord,
EXCEPTION_EXECUTE_HANDLER) {
switch (SavedExceptRec.ExceptionCode)
{
// ...
}
}
 

 

软件异常

 
目前为止我们讨论的都是硬件异常。软件异常的捕获方式和硬件异常完全一样。
 
本部分要讨论的是如何让函数强制抛出软件异常,以作为一种指明错误的方法。需要调用函数 RaiseException。
 
// 抛出一个软件异常
VOID RaiseException(
  // 要抛出异常标识符。
  // 如果定义自己的异常标识符,要遵循windows错误代码定义的标准格式。
  DWORD dwExceptionCode,
 
  // 标志:
  // 0
  // EXCEPTION_NONCONTINUABLE,此时异常过滤程序不能返回EXCEPTION_CONTINUE_EXECUTION,一般用来传递不可恢复的错误信息
  // 此时,过滤程序若返回EXCEPTION_CONTINUE_EXECUTION,会引发EXCEPTION_NONCONTINUABLE_EXCEPTION
  DWORD dwExceptionFlags,
 
  // 异常附件信息,一般不需要,置NULL
  DWORD nNumberOfArguments,
 
  // 异常附件信息,一般不需要,置NULL
  CONST ULONG_PTR *pArguments);

23.2 SEH之异常处理--《Windows核心编程》结构化异常处理的更多相关文章

  1. 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  2. 深入研究 Win32 结构化异常处理(好多相关文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  3. Windows核心编程 第23章 结束处理程序

    第2 3章 结束处理程序 SEH(结构化异常处理) 使用 S E H的好处就是当你编写程序时,只需要关注程序要完成的任务. 如果在运行时发生什么错误,系统会发现并将发生的问题通知你.利用S E H,你 ...

  4. 第23章 SEH结构化异常处理(1)_系统SEH机制

    23.1 基础知识 23.1.1 Windows下的软件异常 (1)中断和异常 ①中断是由外部硬件设备或异步事件产生的 ②异常是由内部事件产生的,可分为故障.陷阱和终止三类. (2)两种异常处理机制: ...

  5. C++Windows核心编程读书笔记

    转自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/71405.shtml "C++Windows核心编程读书笔 ...

  6. 【转】《windows核心编程》读书笔记

    这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入 ...

  7. 【windows核心编程】 第八章 用户模式下的线程同步

    Windows核心编程 第八章 用户模式下的线程同步 1. 线程之间通信发生在以下两种情况: ①    需要让多个线程同时访问一个共享资源,同时不能破坏资源的完整性 ②    一个线程需要通知其他线程 ...

  8. 第23章 SEH结构化异常处理(2)_编译器对系统SEH机制的封装

    23.2 编译器层面对系统SEH机制的封装 23.2.1 扩展的EXCEPTION_REGISTRATION级相关结构:VC_EXCEPTION_REGISTRATION (1)VC_EXCEPTIO ...

  9. 【windows核心编程】 第六章 线程基础

    Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ①    一个是线程的内核 ...

  10. 《Windows核心编程》读书笔记 上

    [C++]<Windows核心编程>读书笔记 这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对 ...

随机推荐

  1. 【win10 系统】idea 修改 Git 密码和账号方法

    https://my.oschina.net/anxiaole/blog/1588664

  2. Linux 文件目录配置及命令总结

    前言 在Linux中,一切皆文件,而每一个文件和目录都是从根目录开始的 Linux文件目录的作用 /bin目录:用来存放二进制可执行命令的目录,用户常用的命令都存在该目录下. /sbin目录:用来存放 ...

  3. [译] kubernetes:kube-scheduler 调度器代码结构概述

    本文翻译自 https://github.com/kubernetes/community/blob/master/contributors/devel/sig-scheduling/scheduli ...

  4. Feign源码解析7:nacos loadbalancer不支持静态ip的负载均衡

    背景 在feign中,一般是通过eureka.nacos等获取服务实例,但有时候调用一些服务时,人家给的是ip或域名,我们这时候还能用Feign这一套吗? 可以的. 有两种方式,一种是直接指定url: ...

  5. MCU芯片设计流程

    MCU设计流程 1.产品开发整体流程 Integrated Product Development(IPD) TR-Technique Review-技术评审 xDCP-管理层决定是否开发 这里的验证 ...

  6. P1914 小书童——凯撒密码

    1.题目介绍 小书童--凯撒密码 题目背景 某蒟蒻迷上了 "小书童",有一天登陆时忘记密码了(他没绑定邮箱 or 手机),于是便把问题抛给了神犇你. 题目描述 蒟蒻虽然忘记密码,但 ...

  7. 【FreeRTOS】内核查找最高优先级就绪任务

    查找最高优先级就绪任务 FreeRTOS\Source\tasks.c #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) /* If confi ...

  8. Laravel - 配置 数据库

  9. Linux-进程管理-ps-kill

  10. [转帖]【SQL SERVER】锁机制

    https://www.cnblogs.com/WilsonPan/p/12618849.html   锁定是 SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制. 基 ...