|
标 题:
【原创】Windows系统程序设计之结构化异常处理
作 者: 北极星2003 时 间: 2006-09-20,20:21 链 接: http://bbs.pediy.com/showthread.php?t=32222 目录: 一、 SEH的概念、特性 二、 SEH的基本使用方法 1、 结束异常程序 (1)try块的自然退出与非自然退出 (2)finally块的清理功能及对程序结构的影响 (3)关键字__leave 2、 异常处理程序 (1)异常处理的基本流程 (2)异常过滤器 (3)全局展开 (4)暂停全局展开 3、 未处理异常(顶层异常处理) 三、 SEH相关数据结构的介绍 1、 EXCEPTION_POINTER结构 2、 EXCEPTION_RECORD结构 3、 EXCEPTOIN_REGISTRATION结构 4、异常处理链结构图 四、 VC++编译器级SEH的具体实现 1、 VC扩展异常帧 2、 VC异常帧堆栈布局 3、 两个实例程序:显示异常帧信息 4、 实例分析及特性介绍 5、 VC中的定层异常处理 6、 VC搜索异常处理程序流程 五、参考资料 -------------------------------------------------------------------------- 前言: 对于这片文章应该是我写的最认真的一篇了,断断续续地写了将近一个月,从最初的复习性的回顾,收集更多资料,反复地整理思路,查阅Windows源码中的相关的源码,设计文章的整体框架,到每一个部分的详细设计,包括流程图的设计。每一个过程都进展的并不顺利,由于时间的关系每星期只能有2~3次的大段时间的在电脑面前,所以思路一直在被打断,整理文章写的并不流畅。有些思路或许已经不知道遗忘在哪个角落,但我也尽量把相关方面的知识点都讲到。本文的重点在于SEH原理方面的介绍,从单纯的使用角度来看,比较简单,但如果适当的使用SEH机制,这在很大程度上与使用者对SEH的理解程度有很大关系,因而对于具体的实现,文中只介绍重点及需要注意的地方,具体使用方法可参见参考文献[1]。 一、 SEH的概念、特性 SEH,结构化异常处理,是作为一种系统机制引入到操作系统中的,本身与语言无关。在我们自己的程序中使用SEH可以让我们集中精力开发关键功能,而把程序中所可能出现的异常进行统一的处理,使程序显得更加简洁且增加可读性。 当在程序中使用SEH时,就变成编译器相关的。其所造成的负担主要由编译程序来承担,例如编译程序会产生一些表(table)来支持SEH的数据结构,还会提供回调函数。 二、 SEH的基本使用方法 1、 结束异常程序 一个结束异常程序能确保调用和执行一个代码块,对应与具体的实现,结束处理程序的结构如下所示: __try { // 受保护的代码 } __finally { // 结束处理程序 } (1)try块的自然退出与非自然退出 try块可能会因为return,goto,异常等非自然退出,也可能会因为成功执行而自然退出。但不论try块是如何退出的,finally块的内容都会被执行。 请看下面两个程序:
通过使用结束处理程序,可以避免return语句的过早执行。当retrun 试图退出try块时,编译程序要保证finally块中的代码首先被执行。这事实上就是一个局部展开的过程,当从try块的过早退出强制控制转移到finally块时,都将引起局部展开。 (2)finally块的清理功能及对程序结构的影响 写过软件的朋友一般都有这样一个影响:在编码的过程中需要加入需要检测,检测功能是否成功执行,若成功的话执行这个,不成功的话需要作一些额外的清理工作,例如释放内存,关闭句柄等。如果检测不是很多的话,倒没什么影响;但若又许多检测,且软件中的逻辑关系比较复杂时,往往需要化很大精力来实现繁芜的检测判断。结果就会使程序看起来结构比较复杂,大大降低程序的可读性,而且程序的体积也不断增大。 事实上可以用SEH 来解决,把一些相关函数的清理代码都放在finally块,只需要在其中加一些适当的判断,不需要回到每个可能失败的地方添加清理代码。下面的FunSampleA函数是一个常规的函数,FunSampleB引入了SEH结束处理程序机制:
这两个函数的功能是一样的。可以看到在FunSampleA中的清理函数(CloseHandle)到处都是,而在FunSampleB中的清理函数则全部集中在finally块,如果在阅读代码时只需看try块的内容即可了解程序流程。这两个函数本身都很小,可以细细体会下这两个函数的区别。 (3)关键字__leave 在try块中使用__leave关键字会使程序跳转到try块的结尾,从而自然的进入finally块。 对于上例中的FunSampleB,try块中的3个return完全可以用__leave来替换。两者的区别是用return会引起try过早退出系统会进行局部展开而增加系统开销,若使用__leave就会自然退出try块,开销就小的多。 但有一种情况下必须使用__leave而不能使用return,即当finally块后还需要执行一定的功能,如下所示:
2、异常处理程序 异常处理程序能在程序发生异常时进行相应的处理,对应与具体的实现,异常处理程序的结构如下所示: __try { // 受保护的代码 } __except ( /*异常过滤器exception filter*/ ) { // 异常处理程序exception handler } (1)异常处理的基本流程(注:此流程图来源于参考资料[1])
(2)异常过滤器 异常过滤器只有三个可能的值(定义在Windows的Excpt.h中): EXCEPTION_EXECUTE_HANDLER EXCEPTION_CONTINUE_SERCH EXCEPTION_CONTINUE_EXECUTION 下面是两种基本的使用方法: 方式一:直接使用过滤器的三个返回值之一 __try { …… } __except ( EXCEPTION_EXECUTE_HANDLER ) { …… } 方式二:自定义过滤器 __try { …… } __except ( MyFilter( GetExceptionCode() ) ) { …… } LONG MyFilter ( DWORD dwExceptionCode ) { if ( dwExceptionCode == EXCEPTION_ACCESS_VIOLATION ) return EXCEPTION_EXECUTE_HANDLER ; else return EXCEPTION_CONTINUE_SEARCH ; } (3)全局展开 首先来看一下全局展开的基本流程(此流程图来源于参考资料[1]):
接下来看一个全局展开的实例(源码GlobalUnwindSample.cpp):
代码:
#include <iostream.h>
#include <windows.h>
static unsigned int nStep = 1 ;
void Function_B ()
{
int x, y = 0 ;
__try {
x = 5 / y ; // 引发异常
}
__finally
{
cout << "Step " << nStep++ << " : 执行Function_B的finally块的内容" << endl ;
}
}
void Function_A ( )
{
__try {
Function_B () ;
}
__finally
{
cout << "Step " << nStep++ << " : 执行Function_A的finally块的内容" << endl ;
}
}
long MyExcepteFilter ( )
{
cout << "Step " << nStep++ << " : 执行main的异常过滤器" << endl ;
return EXCEPTION_EXECUTE_HANDLER ;
}
int main ()
{
__try {
Function_A () ;
}
__except ( MyExcepteFilter() )
{
cout << "Step " << nStep++ << " : 执行main的except块的内容" << endl ;
}
return 0 ;
}
/*输出结果:
Step 1 : 执行main的异常过滤器
Step 2 : 执行Function_B的finally块的内容
Step 3 : 执行Function_A的finally块的内容
Step 4 : 执行main的except块的内容
*/
(4)暂停全局展开 如果程序中出现异常,且已经找到过滤器值为EXCEPTION_EXECUTE_HANDLER所对应的try块,此时系统会进行全局展开,在正常情况下,系统会执行该try块以内的所有finally过程,然后再执行该try块对应的异常处理过程。但如果在某个finally中放入一个return,就可以阻止全局展开。 3、未处理异常(顶层异常处理) 当软件中出现异常,而在你的程序中没有相应的异常处理程序,此时就形成了未处理异常。此时系统会弹出异常提示对话框,并可以结束进程。 显示异常对话框是这个功能具体是在UnhandledExceptionFilter中实现的,在启动进程、线程时,系统会安装一个最顶层的异常处理try-except结构,如下所示: BaseProcessStart用于进程的主线程,而BaseThreadStart用于其他线程。当异常产生时,如果程序中没有相应的异常处理程序或者全都返回EXCEPTION_CONTINUE_SEARCH时,就会自动调用UnhandleExceptionFilter。 三、 SEH相关数据结构的介绍 1、EXCEPTION_POINTER结构 2、EXCEPTION_RECORD结构 3、EXCEPTOIN_REGISTRATION结构 4、异常处理链结构图 四、VC++编译器级SEH的具体实现 1、VC扩展异常帧 2、VC异常帧堆栈布局 3、两个实例程序:显示异常帧信息(详见源码ShowExcptFrame1.cpp,ShowExcptFrame2.cpp) 对上面的结构不熟悉也没关系,下面通过实例来加深理解。
代码:
//
// 显示ScopeTable信息
//
void SEHShowScopeTable( PVC_EXCEPTION_REGISTRATION pVCExcRec )
{
printf( "Frame: %08X Handler: %08X Prev: %08X Scopetable: %08X/n", /
pVCExcRec, pVCExcRec->handler, pVCExcRec->prev, pVCExcRec->scopetable );
PSCOPETABLE pScopeTableEntry = pVCExcRec->scopetable;
for ( int i = 0; i <= pVCExcRec->trylevel; i++ )
{
printf( " scopetable[%u] PrevTryLevel: %08X "
"filter: %08X __except: %08X/n", i,
pScopeTableEntry->previousTryLevel,
pScopeTableEntry->lpfnFilter,
pScopeTableEntry->lpfnHandler );
pScopeTableEntry++;
}
printf( "/n" );
}
//
//显示异常帧信息
//
void SEHShowExcptFrames( )
{
PVC_EXCEPTION_REGISTRATION pVCExcRec;
// 取得异常帧链首地址,保存在pVCExcRec
__asm mov eax, FS:[0]
__asm mov [pVCExcRec], EAX
// 遍历异常帧链
while ( (unsigned)pVCExcRec != 0xFFFFFFFF )
{
SEHShowScopeTable( pVCExcRec );
pVCExcRec = (PVC_EXCEPTION_REGISTRATION)(pVCExcRec->prev);
}
}
输出结果: 嵌套异常结构示例二: 输出结果: 4、实例分析及特性介绍(比较分析,注意结构示例与结果之间的联系) [1] 上面这个两个结果中各有4个异常帧。 [2] 他们的后两条异常帧都是一致的,变化的只是前两个异常帧 [3] 都是两重函数调用,mainFunction,每一重调用都以为着增加一个异常帧,其中的第一个帧代表最内层异常块,即Function函数的异常帧(相对于显示异常帧信息的函数来说),第二帧代表main函数的异常帧。 [4] scopetable域的项数与该函数中的异常块数目相关,示例一中的Function函数中有三个嵌套的异常块,因而第一帧scopetable中有三项;示例二中的Function函数中有四个异常块,因而第二帧scoptable中有四项。 [5] 首先来看示例一,Function函数中的三个异常块是嵌套的,所以在scopetable[2]中的PrevTryLevel=1,表示当前异常块在帧中的索引为2,与它相邻的外层异常块的索引为1,这个异常块的异常过滤器地址为filter:00401204,而异常处理回调函数的地址为__except: 00401207;因而在示例一的第一帧scopetable中的三项形成一个链表(这也比较好理解:三个异常块嵌套)。再来看示例二,还是看第一帧,自己分析下,是否形成了两个链表。到这里,对于scoptable中的PrevTryLevel的意义应该清楚了。如果对于这部分还不是很理解的话可以通过增加或修改程序中异常块、或者再增加几层函数调用,然后观察分析输出结果。 [6] 不知道大家有没有发现EXCEPTION_REGISTRATION结构中有个handler,而在SCOPETABLE中也有个lpfnHandler(即上面例子输出结果中的__except项),这两个都是异常处理回调函数的地址,它们之间到底有什么联系和区别?示例中前三帧的handler地址都是004014B0,why? 这个也不难回答,scopetable中的lpfnHandler是用于用户自定义的异常处理函数,而EXCEPTION_REGISTRATION的handler是用于系统默认的异常处理程序,即通常说的顶层异常处理回调函数。 [7] 分析到这里,我只分析了4帧中的两帧,至于后两帧的作用,这一点将会在后文中讲解。 5、VC中的顶层异常处理 是否还记得在介绍第二部分的未处理异常时提到的BaseProcessStart,BaseThreadStart这两个函数。在启动主/次线程时,系统会把整个线程置于一个异常块中,即顶层异常处理体。在上文显示异常帧的两个例子中的第四帧正是来源于此。 在默认情况下,VC程序的入口点由运行时库函数mainCRTStartup和WinMainainCRTStartup来实现(如果对这方面不熟悉的话,请参见参考资料[5]),其中又一次对将要调用的用户程序入口main,WinMain(默认情况)置于异常处理体中,在上文显示异常帧的两个例子中的第三帧来自这里。 对于这两个入口函数,CRT中有相应的源码,大致结构经简化如下所示: 现在可以来总结下,VC编译器中默认情况(即我们的程序不显示的引入SEH)下的异常块结构示例: 其中的BaseProcessStart,WinMainCRTStartup这两个函数并非固定,需要是视具体情况而定,这个只是用做示例。 到这里,对于上小节中的遗留下来的关于第三和第四个异常帧的问题已经解决了。 6、VC搜索异常处理程序流程 在介绍第三部分SEH相关数据结构时,已经介绍了异常处理链的结构,但针对VC中的异常处理,异常帧的扩展包括scopetable结构的引入使得支持嵌套的异常结构,因而当出现异常时寻找相应的异常处理程序的过程相对来说变的更加复杂。VC搜索异常处理程序流程如下所示: 【参考文献】 [1]. Windows核心编程 Jeffrey Richter著 机械工业出版社 [2]. A Crash Course on the Depths of Win 32® Structured Exception Handling http://www.microsoft.com/msj/0197/Exception/Exception.aspx [3]. Windows 2000 内部揭密 机械工业出版社 [4]. Windows异常处理流程 SoBeIt http://www.nsfocus.net/index.php?act=magazine&do=view&mid=2471 [5]. 在VC中编译、运行程序的小知识点 http://fmddlmyy.home4u.china.com/text3.html |
相关推荐
Windows的异常处理
文章目录Windows的异常处理SEHSEH的终结处理SEH的异常处理过滤表达式嵌套使用异常处理和终结处理SEH的实现机制局部展开全局展开VEHSEH和VEH的区别 Windows的异常处理 Windows系统的异常处理有两种: 结构化异常处理(SEH,Structured Exception Handling) 向量化异常处理(VEH,Vectored Exception Handling) SEH Windows定义了SEH机制来规范异常处理代码的设计(对程序员)和编译(对编译器)。 SEH 提供了
JAVA超详细:异常类型及处理(含实例)
异常的产生 Java 程序运行期间出现了一个错误。这个错误可能是由于文件包含了错误信息,或者网络连接出现问题造成的,也有可能是因为使用无效的数组下标, 或者试图使用一个没有被赋值的对象引用而造成的。此时用户希望: 返回到一种安全状态,并能够让用户执行一些其他的命令 允许用户保存所有操作的结果,并以妥善的方式终止程序 常见的错误有这几种类型: 用户输入的错误 设备错误 物理限制 代码错误 异常的分类 Throwable类 Throwable类是所有异常类的子类,所有的异常..
使用 Unity Sentis 和 Compute Shader,det_10g.onnx 进行高效人脸五官定位
在计算机视觉领域,人脸五官定位是一个重要的任务。本文将介绍如何使用 Unity Sentis 和 Compute Shader,结合模型,实现高效的人脸五官定位。我们将详细讲解每一步骤,并提供完整的代码示例。
Windows核心编程:结构化异常处理
作者:shenzi链接:http://blog.youkuaiyun.com/shenzi Windows核心编程:结构化异常处理 结构化异常处理(structured exception handling,SEH)带来的好处是我们在写代码时,可以先集中精力完成软件的正常工作流程。如果在运行的时候出现了什么问题,系统
手把手教你用Boot-Repair快速修复Ubuntu启动分区缺失(附详细命令行步骤)
本文详细介绍了如何使用Boot-Repair工具快速修复Ubuntu启动分区缺失问题,包括诊断、应急准备、自动修复和手动命令操作。通过grub rescue模式下的关键命令和Boot-Repair的图形界面操作,帮助用户高效解决启动分区故障,确保系统恢复正常运行。
Python、Selenium实现问卷星自动填写(内含适配个人问卷的方法)
使用Python、Selenium实现问卷星自动填写,内含适配个人问卷的方法。
结构化异常处理
结构化异常处理(Structured Exception Handling,简称SEH)是Windows操作系统处理程序错误或异常技术。SEH是Windows操作系统的一种系统机制,与特定的程序设计语言无关。 Inter公司在从386开始的IA-32家族处理器中引入了中断(Interrupt)和异常(Exception)的概念。中断时由外部硬件设备或异步事件产生的。异常是由内
让 AI 变聪明的 20 条指令:如何让大模型回答得更精准、更专业、更实事求是
让 AI 变聪明,本质上不是写几句神奇咒语,而是把你的任务表达得更清楚,把事实标准设得更严格,把输出格式规定得更明确,把不确定性管理得更认真,把历史对话利用得更充分。大模型的强项是语言生成、信息组织、模式归纳和方案扩展;弱项是事实核验、实时更新、责任判断和对模糊意图的准确猜测。好的指令,就是在用人的判断弥补模型的弱点,让模型把强项发挥在正确的地方。
windows核心编程---结构化异常处理
-终止处理程序 结构化异常处理的好处是,写代码时,先集中精力完成正常流程。将功能处理,异常处理分开。进入,离开异常处理代码块时,编译器须生成一些特殊代码,产生一些关于支持SEH的数据结构表,提供回调函数,其它。 不同编译器对SEH的实现不尽相同,本章采用Microsoft Visual C++编译器规定的用法。C++异常处理:catch,throw。 和结构化异常处理形式不同。SEH: 终止
3DMax高效建模与人工智能:一场颠覆传统创作的效率革命
AI与3DMax的结合,不仅是工具效率的升级,更是一场创作范式的革命。从“手动雕刻”到“智能共生”,设计师的角色正从执行者转向决策者。正如网友所言:“AI不是替代人类,而是赋予凡人以神笔。”在这场变革中,唯有拥抱技术、深耕创意,方能在数字艺术的浪潮中立于潮头。参考文献与工具推荐技术原理:火星时代资讯《人工智能和三维建模》工具实测:哔哩哔哩《tyDiffusion 3dsMax应用》9、Tripo AI官网教育案例:原创论文《人工智能技术介入三维动画课程建设》
C#上位机开发入门:从基础到进阶的全方位指南
C#作为一门现代化编程语言,具备强大的功能和广泛的生态支持,适合用于开发各种类型的上位机应用。通过学习并掌握UI开发、通信协议、多线程编程、数据库操作等技能,你可以打造出稳定、易用、性能优异的上位机软件。
深入解析结构化异常处理(SEH) - by Matt Pietrek
深入解析结构化异常处理(SEH) - by Matt Pietrek
2022中山大学计算机技术专硕考研初试、复试经验帖
个人简介 推荐几个我自己感觉对考研非常有帮助的小助手吧。可以帮助节省时间! 考研时间规划总览 初试篇 数学 英语 政治 408 复试篇 机试 笔试 面试 关于选导师 个人感言
深入浅出Windows异常处理
程序执行过程中难免会发生错误,例如访问非法内存地址或除数为零等,CPU负责检测此类错误并转交给Windows处理,此类错误称为硬件异常。除了硬件异常,操作系统和应用程序还可以显式抛出一个异常,称为软件异常。异常处理是指异常发生后(包括硬件异常和软件异常),Windows或应用程序所执行的一段处理逻辑,这种处理逻辑旨在应对和解决异常情况,从而确保系统或应用程序能够继续运行或安全地终止。 异常发生时,如果程序中没有显式设置相应的异常处理代码,系统通常会启动Windows错误报告(Windows Error Re
30. 如何使用 SEGW 的 redefine 功能对 SAP 标准 OData 服务进行扩展
SAP 发布了很多标准的 Fiori 应用,可以通过笔者的文章里提到的进行浏览。比如 My Opportunities 就是 SAP CRM 领域的一个 Fiori 应用:下图是 Opportunity 明细页面的 Info 标签页:假设我们想在这些标准字段的末尾增添一个新字段 Created By,用来维护当前 Opportunity 的创建者。
文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况
主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。
ReactOS异常处理:结构化异常处理的Windows兼容性
在操作系统开发中,异常处理机制是确保系统稳定性和可靠性的核心组件。ReactOS作为一个开源的Windows兼容操作系统,其结构化异常处理(Structured Exception Handling,SEH)实现对于保持与Windows应用程序的二进制兼容性至关重要。 本文将深入探讨ReactOS的异常处理架构,分析其SEH实现机制,并通过代码示例展示如何实现与Windows完全兼容的异常处理行...
ssm框架搭建
这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载
SEH结构化异常处理
Matt Pietrek 著董岩 译Victor 转载自 Xfocus 并整理在所有 Win32 操作系统提供的机制中,使用最广泛的未公开的机制恐怕就要数结构化异常处理(structured exception handling,SEH)了。一提到结构化异常处理,可能就会令人想起 _try、_finally 和 _except 之类的词儿。在任何一本不错的 Win32 书中都会有对 SEH 详
Probability and Statistics - 4th edition
Probability and Statistics - 4th edition
计算机控制技术及应用
《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。
Windows 系统编程初探 (四)结构化异常处理之一:SEH的基本原理与进程相关异常处理
上面的内容只是一些基础知识,虽然简单,但有必要了解一下。现在,我将正式开始我的第一个专题:结构化异常处理(SEH)。SEH 是 Windows 系统提供的功能,跟开发工具无关。值得一提的是,VC 将 SEH 进行了封装,也就是我们平常用到的 __try{}__except(){} 和 __try{}__finally{},我没有研究过它的实现方法,这里也不进行讨论,而我将要讲述的是
Windows系统编程(六)异常处理
在Windows下,有两种异常:1.结构化异常 SEH2.向量化异常 VEH接下来我们学习这两种异常。
关于windows中的结构化异常处理
微软在windows中引入了结构化异常处理(SEH)的主要动机是为了便于操作系统的开发。操作系统的开发人员使用SEH,使得系统更加强壮。我们也可以使用SEH,使我们自己的程序更加强壮。下面分情况说明:1、结束处理程序:SEH中引入了try、finally对来进行结束化程序处理。具体的执行顺序可参考对finally的两种分类:a)从try块中进入finally块的正常控制流。
结构化异常处理SEH
结构化异常处理(seh)(Z)毕业的事情终于要搞定了,几个月前就答应要写这么一个文章,现在补上.结构化异常处理是一种操作系统提供的机制, 用来优化程序的结构,提供更加健壮的程序执行环境.试想想你写程序不用考虑哪里有个内存访问错误,哪里有个空指针等等一类的错误,一直按照程序的逻辑结构 向下写,而不用去检查函数是否成功,这会是多么愉悦的事情(这个乃是seh的宣传词,不代表我的观点,这里
2645



被折叠的 条评论
为什么被折叠?



