异常处理程序和软件异常

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章 异常处理程序和软件异常相关推荐

  1. 异常处理程序和软件异常——Windows核心编程学习手札之二十四

    异常处理程序和软件异常 --Windows核心编程学习手札之二十四 CPU负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对错误的反应,CPU引发的异常称为硬件异常(hardwar ...

  2. C24、异常处理程序和软件异常

    __try, __except(exception filter) 一.异常过滤器(exception filter)和异常处理程序 一.异常过滤器中使用逗号(,)操作符.从左到右执行,返回最右边表达 ...

  3. 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 ...

  4. Windows核心编程 第十五章 在应用程序中使用虚拟内存

    第1 5章 在应用程序中使用虚拟内存 Wi n d o w s提供了3种进行内存管理的方法,它们是: • 虚拟内存,最适合用来管理大型对象或结构数组. • 内存映射文件,最适合用来管理大型数据流(通常 ...

  5. Windows核心编程:第14章 探索虚拟内存

    Github https://github.com/gongluck/Windows-Core-Program.git //第14章 探索虚拟内存.cpp: 定义应用程序的入口点. //#includ ...

  6. Windows核心编程 第十九章 DLL基础

    第1 9章 D L L基础 这章是介绍基本dll,我就记录一些简单应用,dll的坑点以及扩展后面两章会说,到时候在总结. 自从M i c r o s o f t公司推出第一个版本的Wi n d o w ...

  7. Windows核心编程:第7章 线程调度、优先级和关联性

    Github https://github.com/gongluck/Windows-Core-Program.git //第7章 线程调度.优先级和关联性.cpp: 定义应用程序的入口点. //#i ...

  8. Windows核心编程 第十四章 虚拟内存

    第1 4章 虚 拟 内 存 <这一章没啥,是说的几个内存相关的函数 > 14.1 系统信息 许多操作系统的值是根据主机而定的,比如页面的大小,分配粒度的大小等.这些值决不应该用硬编码的形式 ...

  9. Windows核心编程 第十二章 纤程

    第1 2章 纤 程 M i c r o s o f t公司给Wi n d o w s添加了一种纤程,以便能够非常容易地将现有的 U N I X服务器应用程序移植到Wi n d o w s中.U N I ...

最新文章

  1. 基于Android Studio搭建Android应用开发环境
  2. android纯白背景加灰,Android背景颜色设置为灰色而不是@android:颜色/白色
  3. Mysql中explain命令查看语句执行概况
  4. C语言 | 字符数组
  5. ccs6 linux安装教程,【图片】【吧主帖】在LINUX(ubuntu)系统下装CCSv6方法(原创)【dsp吧】_百度贴吧...
  6. 预览ExtJS 4.0的新功能(四):焕然一新的Store/Proxy
  7. 华为关于gvrp 的配置,一般步骤以及应用。
  8. Kafka.net使用编程入门
  9. 自己的包增加为第三方包,使用Eclipse环境报Unresolved import错误(pycharm可用正常引用)...
  10. 面试问到处理过什么棘手问题_为什么调节人工智能如此棘手?
  11. IP地址-子网掩码-默认网关之间的关系
  12. oracle+dba+网课,[Oracle] 蓬动Oracle教程 DBA培训视频实战精品课及开发转Oracle 共52课...
  13. 2022年财富世界500强研究报告
  14. seaborn小提琴图
  15. 飞腾CPU体系结构(八)
  16. 2022微信支付v3 - Native
  17. 淘宝优惠券查询API接口
  18. 高德地图之反地理编码、线路规划、天气查询
  19. Vue 中引入markdown富文本编辑器并根据md格式渲染
  20. centos 升级glibcc++

热门文章

  1. 原生JS DOM操作方法汇总
  2. 前端那些事之weex
  3. 集成学习算法总结----Boosting和Bagging
  4. mysql基础命令学习笔记
  5. Linux常用系统管理命令(top、free、kill、df)
  6. 傲笑九天志在必得,卧薪尝胆勇者无畏.
  7. some language grammars
  8. 汇编学习心得(二)关于字符的处理
  9. Feature Schema中Module和File节点属性含义的解释
  10. Android 开发 Activity里获取View的宽度和高度 转载