Windows核心编程 第2 4章 异常处理程序和软件异常
异常处理程序和软件异常
C P U引发的异常,就是所谓的硬件异常(hardware exception)。操作系统和应用程序
也可以引发相应的异常,称为软件异常(software exception)。
当出现一个硬件或软件异常时,操作系统向应用程序提供机会来考察是什么类型的异常被引发,并能够让应用程序自己来处理异常。下面就是异常处理程序的文法:
注意- - e x c e p t关键字。每当你建立一个t r y块,它必须跟随一个f i n a l l y块或一个e x c e p t块。一个try 块之后不能既有f i n a l l y块又有e x c e p t块。但可以在t r y - e x c e p t块中嵌套t r y - f i n a l l y块,反过来也可以。
与结束处理程序(__try{}__finally{})不同,异常过滤器( exception filter)和异常处理程序是通过操作系统直接执行的,编译程序在计算异常过滤器表达式和执行异常处理程序方面不做什么事。
尽管在结束处理程序的t r y块中使用r e t u r n、g o t o、c o n t i n u e和b r e a k语句遭到强烈地反对,但在异常处理程序的t r y块中使用这些语句不会产生速度和代码规模方面的不良影响。这样的语句出现在与e x c e p t块相结合的t r y块中不会引起局部展开的系统开销。
例子1(可以直接捕获到异常,不会内存您访问故障的窗体,__try{}__finally{}会弹窗)
解释__except()里面的三种参数:
EXCEPTION_EXECUTE_HANDLER
这个值的意思是要告诉系统:“我认出了这个异常。即,我感觉这个异常可能在某个时候发生,我已编写了代码来处理这个问题,现在我想执行这个代码。”
FuncOren1中/0导致硬件中断,然后开始回溯到FuncOstimpy1发现有except(同时参数是EXCEPTION_EXECUTE_HANDLER),然后就开始继续执行finally对finally开始全局展开,执行完成finally代码之后回溯到上一层 FuncOstimpy1里去执行__except代码。结果是先输出finally然后输出except。
暂停全局展开:书上说是在类似上面的例子中,如果finally里面有return语句就会终止全局展开,然而没编译过去。
EXCEPTION_CONTINUE_EXECUTION
这里,首先遇到的问题是在我们试图向 p c h B u ff e r所指向的缓冲区中放入一个字母‘ J’时发生的。因为这里没有初始化p c h B u ff e r,使它指向全局缓冲区g _ s z B u ff u r。p c h B u ff e r实际指向N U L L。C P U将产生一个异常,并计算与异常发生的 t r y块相关联的e x c e p t块的异常过滤器。在e x c e p t块中,对O i l F i l t e r函数传递了p c h B u ff e r变量的地址。
当O i l F i l t e r获得控制时,它要查看* p p c h B u ff e r是不是N U L L,如果是,把它设置成指向全局缓冲区g _ s z B u ff e r。然后这个过滤器返回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 _ C O N T I N U E _ E X E C U T I O N时,系统跳回到产生异常的指令,试图再执行一次。这一次,指令将执行成功,字母‘J’将放在g _ s z B u ff e r的第一个字节。
随着代码继续执行,我们又在 t r y块中碰到除以0的问题。系统又要计算过滤器的值。这一次,O i l F i l t e r看到* p p c h B u ff e r不是N U L L,就返回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块中的代码。这会显示一个消息框,用文本串报告发生了异常。
实际执行结果是输出了一次Function completed 但第一个MessageBoxA(NULL ,pchBuffer ,"tit" ,MB_OK); 并没有执行
EXCEPTION_CONTINUE_SEARCH
取值 E X C E P T I O N _CONTINUE_ SEARCH。这个标识符是告诉系统去查找前面与一个 e x c e p t块相匹配的t r y块,并调用这个t r y块的异常处理器。
GetExceptionCode
一个异常过滤器在确定要返回什么值之前,必须分析具体情况。例如,异常处理程序可能知道发生了除以0引起的异常时该怎么做,但是不知道该如何处理一个内存存取异常。异常过滤器负责检查实际情况并返回适当的值。
软件异常
迄今为止,我们一直在讨论硬件异常,也就是 C P U捕获一个事件并引发一个异常。在代码中也可以强制引发一个异常。这也是一个函数向它的调用者报告失败的一种方法。传统上,失败的函数要返回一些特殊的值来指出失败。函数的调用者应该检查这些特殊值并采取一种替代的动作。通常,这个调用者要清除所做的事情并将它自己的失败代码返回给它的调用者。这种错误代码的逐层传递会使源程序的代码变得非常难于编写和维护。
另外一种方法是让函数在失败时引发异常。用这种方法,代码更容易编写和维护,而且也执行得更好,因为通常不需要执行那些错误测试代码。实际上,仅当发生失败时也就是发生异常时才执行错误测试代码。
但令人遗憾的是,许多开发人员不习惯于在错误处理中使用异常。这有两方面的原因。第一个原因是多数开发人员不熟悉S E H。即使有一个程序员熟悉它,但其他程序员可能不熟悉它。如果一个程序员编写了一个引发异常的函数,但其他程序员并不编写S E H框架来捕获这个异常,那么进程就会被操作系统结束。
开发人员不使用S E H的第二个原因是它不能移植到其他操作系统。许多公司的产品要面向多种操作系统,因此希望有单一的源代码作为产品的基础,这是可以理解的。 S E H是专门针对Wi n d o w s的技术。
本段讨论通过异常返回错误有关的内容。首先,看一看 Windows Heap函数,例如H e a p C r e a t e、h e a p A l l o c等。回顾第1 8章的内容,我们知道这些函数向开发人员提供一种选择。通常当某个堆( h e a p)函数失败,它会返回 N U L L来指出失败。然而可以对这些堆函数传递H E A P _ G E N E R AT E _ E X C E P T I O N S标志。如果使用这个标志并且函数失败,函数不会返回N U L L,而是由函数引发一个 S TAT U S _ N O _ M E M O RY软件异常,程序代码的其他部分可以用S E H框架来捕获这个异常。
如果想利用这个异常,可以编写你的 t r y块,好像内存分配总能成功。如果内存分配失败,可以利用e x c e p t块来处理这个异常,或通过匹配 t r y块与f i n a l l y块,清除函数所做的事。这非常方便。
程序捕获软件异常采取的方法与捕获硬件异常完全相同。也就是说,前一章介绍的内容可同样适用于软件异常。
本节重讨论如何让你自己的函数引发软件异常,作为指出失败的方法。实际上,可以用类似于微软实现堆函数的方法来实现你的函数:让函数的调用者传递一个标志,告诉函数如何指出失败。引发一个软件异常很容易,只需要调用R a i s e E x c e p t i o n函数:
第一个参数 d w E x c e p t i o n C o d e是标识所引发异常的值。 H e a p A l l o c函数对这个参数设定S TAT U S _ N O _ M E M O RY。如果程序员要定义自己的异常标识符,应该遵循标准 Wi n d o w s错误代码的格式,像Wi n E r r o r. h文件中定义的那样。参阅表2 4 - 1。
如果要建立你自己的异常代码,要填充D W O R D的4个部分:
R a i s e E x c e p t i o n的第二个参数 d w E x c e p t i o n F l a g s,必须是 0或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 _CONTINUE _EXECUTION来响应所引发的异常是否合法。如果没有向 R a i s e E x c e p t i o n传递EXCEPTION_ NONCONTINUABLE参数值,则过滤器可以返回 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。正常情况下,这将导致线程重新执行引发软件异常的同一 C P U指令。但微软已做了一些动作,所以在调用R a i s e E x c e p t i o n函数之后,执行会继续进行。
如果你向R a i s e E x c e p 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标志,你就是在告诉系统,你引发异常的类型是不能被继续执行的。这个标志在操作系统内部被用来传达致命(不可恢复)的错误信息。另外,当 H e a p A l l o c引发S TAT U S _ N O _ M E M O RY软件异常时,它使用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 _ N O N C O N T I N U A B L E并返回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。
当程序在处理一个异常的时候,有可能又引发另一个异常。比如说,一个无效的内存存取有可能发生在一个f i n a l l y块、一个异常过滤器、或一个异常处理程序里。当发生这种情况时,系统压栈异常。回忆一下G e t E x c e p t i o n I n f o r m a t i o n函数。这个函数返回EXCEPTION_ POINTERS结构的地址。E X C E P T I O N _ P O I N T E R S的E x c e p t i o n R e c o r d成员指向一个EXCEPTION_ R E C O R D结构,这个结构包含另一个 E x c e p t i o n R e c o r d成员。这个成员是一个指向另外的 E X C E P T I O N _R E C O R D的指针,而这个结构包含有关以前引发异常的信息。
通常系统一次只处理一个异常,并且E x c e p t i o n R e c o r d成员为N U L L。然而如果处理一个异常的过程中又引发另一个异常,第一个E X C E P T I O N _ R E C O R D结构包含有关最近引发异常的信息,并且这个E X C E P T I O N _ R E C O R D结构的E x c e p t i o n R e c o r d成员指向以前发生的异常的E X C E P T I O N _R E C O R D结构。如果增加的异常没有完全处理,可以继续搜索这个 E X C E P T I O N _ R E C O R D结构的链表,来确定如何处理异常。
R a i s e E x c e p t i o n的第三个参数n N u m b e r O f A rg u m e n t s和第四个参数p A rg u m e n t s,用来传递有关所引发异常的附加信息。通常,不需要附加的参数,只需对 p A rg u m e n t s参数传递N U L L,这种情况下, R a i s e E x c e p t i o n函数忽略 n N u m b e r O f A rg u m e n t s参数。如果需要传递附加参数,n N u m b e r O f A rg u m e n t s参数必须规定由p A rg u m e n t s参数所指向的U L O N G _ P T R数组中的元素数目。这个数目不能超过E X C E P T I O N _ M A X I M U M _ PA R A M E T E R S,EXCEPTION_ MAXIMUM_
PARAMETERS 在Wi n N T. h中定义成1 5。
在处理这个异常期间,可使异常过滤器参照 E X C E P T I O N _ R E C O R D结构中的 N u m b e rP a r a m e t e r s和E x c e p t i o n I n f o r m a t i o n成员来检查n N u m b e r O f A rg u m e n t s和p A rg u m e n t s参数中的信息。
你可能由于某种原因想在自己的程序中产生自己的软件异常。例如,你可能想向系统的事件日志发送通知消息。每当程序中的一个函数发现某种问题,你可以调用 R a i s e E x c e p t i o n并让某些异常处理程序上溯调用树查看特定的异常,或者将异常写到日志里或弹出一个消息框。你还可能想建立软件异常来传达程序内部致使错误的信息。
Windows核心编程 第2 4章 异常处理程序和软件异常的更多相关文章
- 【windows核心编程】 第六章 线程基础
Windows核心编程 第六章 线程基础 欢迎转载 转载请注明出处:http://www.cnblogs.com/cuish/p/3145214.html 1. 线程的组成 ① 一个是线程的内核 ...
- Windows核心编程:第4章 进程
Github https://github.com/gongluck/Windows-Core-Program.git //第4章 进程.cpp: 定义应用程序的入口点. // #include &q ...
- Windows核心编程 第2 5章 未处理异常和C ++异常(上)
未处理异常和C + +异常(上) 前一章讨论了当一个异常过滤器返回 E X C E P T I O N _ C O N T I N U E _ S E A R C H时会发生什么事情.返回EXCEPT ...
- Windows核心编程 第十五章 在应用程序中使用虚拟内存
第1 5章 在应用程序中使用虚拟内存 Wi n d o w s提供了3种进行内存管理的方法,它们是: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常 ...
- Windows核心编程:第14章 探索虚拟内存
Github https://github.com/gongluck/Windows-Core-Program.git //第14章 探索虚拟内存.cpp: 定义应用程序的入口点. // #inclu ...
- Windows核心编程:第11章 Windows线程池
Github https://github.com/gongluck/Windows-Core-Program.git //第11章 Windows线程池.cpp: 定义应用程序的入口点. // #i ...
- Windows核心编程:第7章 线程调度、优先级和关联性
Github https://github.com/gongluck/Windows-Core-Program.git //第7章 线程调度.优先级和关联性.cpp: 定义应用程序的入口点. // # ...
- Windows核心编程:第6章 线程基础
Github https://github.com/gongluck/Windows-Core-Program.git //第6章 线程基础.cpp: 定义应用程序的入口点. // #include ...
- Windows核心编程:第5章 作业
Github https://github.com/gongluck/Windows-Core-Program.git //第5章 作业.cpp: 定义应用程序的入口点. // #include &q ...
随机推荐
- Java8 BiFunction 简单用用
最近来了新公司,主要用到了ElasitcSearch,大家都知道在底层查询代码中往往需要判断传入某个参数是否为空来判断设置查询,例如下方代码: BoolQueryBuilder query = Que ...
- Spring如何解决循环依赖
一.什么是循环依赖 多个bean之间相互依赖,形成了一个闭环. 比如:A依赖于B.B依赖于c.c依赖于A 通常来说,如果问spring容器内部如何解决循环依赖, 一定是指默认的单例Bean中,属性互相 ...
- 自己整理的acm模板
第一次上传: 链接:点我下载 大部分常用的模板都弄了,剩下的坑以后再补... 第二次上传: 链接:点我下载 更新内容:新增ST表.分块 第三次上传: 链接:点我下载 更新内容:新增AC自动机,修改权值 ...
- python-链队列的实现
7 class Node(object): 8 def __init__(self,data): 9 self.data = data 10 self.next = None 11 12 class ...
- UML类图画法整理
一 类图画法 1.类图的概念 显示出类.接口以及他们的静态结构和关系,用于描述系统的结构化设计. 2.类 类是对一组具有相同属性.操作.关系和语义对象的抽象,是面向对象的核心,包括名称.属性和方法.如 ...
- switch case语句,switch case用法详解
switch 是"开关"的意思,它也是一种"选择"语句,但它的用法非常简单.switch 是多分支选择语句.说得通俗点,多分支就是多个 if. 从功能上说,sw ...
- 前端知识-CS-01
一.选择器 通过什么方式来定位 1.sytle标签 style标签功能:写css样式的sytle标签的几种写法:1.可以在head里面添加一个style标签 2.在head标签中 通过link标签,引 ...
- ABP 适用性改造 - 精简 ABP CLI 生成的项目结构
Overview 不管是公司或者个人都会有不同的开发习惯,通过建立项目模板,既可以使开发人员聚焦于业务功能的开发,也可以在一定程度上统一不同开发人员之间的开发风格.在使用 ABP 框架的过程中,对于 ...
- Springboot进行Http接口交互实现邮件告警
本项目采用idea编辑器,依赖maven环境,相关搭建请自行百度一.引入相关依赖 本文Http接口交互使用hutool工具类与阿里FastJson解析报文. <dependencies&g ...
- 第3 章 : Kubernetes 核心概念
Kubernetes 核心概念 本文整理自 CNCF 和阿里巴巴联合举办的云原生技术公开课的课时 3:Kubernetes 核心概念.本次课程中,阿里巴巴资深技术专家.CNCF 9个 TCO 之一 李 ...