Windows核心编程024.pdf

上传人:qwe****56 文档编号:70023374 上传时间:2023-01-14 格式:PDF 页数:20 大小:1.02MB
返回 下载 相关 举报
Windows核心编程024.pdf_第1页
第1页 / 共20页
Windows核心编程024.pdf_第2页
第2页 / 共20页
点击查看更多>>
资源描述

《Windows核心编程024.pdf》由会员分享,可在线阅读,更多相关《Windows核心编程024.pdf(20页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、下载第2 4章异常处理程序和软件异常异常是我们不希望有的事件。在编写程序的时候,程序员不会想去存取一个无效的内存地址或用0来除一个数值。不过,这样的错误还是常常会发生的。C P U负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对这些错误的反应。C P U引发的异常,就是所谓的硬件异常(hardware exception)。在本章的后面,我们还会看到操作系统和应用程序也可以引发相应的异常,称为软件异常(software exception)。当出现一个硬件或软件异常时,操作系统向应用程序提供机会来考察是什么类型的异常被引发,并能够让应用程序自己来处理异常。下面就是异常处理

2、程序的文法:注意-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块,反过来也可以。24.1 通过例子理解异常过滤器和异常处理程序与结束处理程序(前一章讨论过)不同,异常过滤器(exception filter)和异常处理程序是通过操作系统直接执行的,编译程序在计算异常过滤器表达式和执行异常处理程序方面不做什么事。下面几节的内容举例说明t r

3、 y-e x c e p t块的正常执行,解释操作系统如何以及为什么计算异常过滤器,并给出操作系统执行异常处理程序中代码的环境。24.1.1 Funcmeister1这里是一个t r y-e x c e p t i o n块的更具体的例子。在F u n c m e i s t e r 1的t r y块中,只是把一个0赋给d w Te m p变量。这个操作决不会造成异常的引发,所以e x c e p t块中的代码永远不会执行。注意这与 t r y-f i n a l l y行为的不同。在d w Te m p被设置成0之后,下一个要执行的指令是r e t u r n语句。尽管在结束处理程序的t r

4、 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块中不会引起局部展开的系统开销。24.1.2 Funcmeister2让我们修改这个函数,看会发生什么事情:F u n c m e i s t e r 2中,t r y块中有一个指令试图以0来除5。C P U将捕捉这个事件,并引发一个硬件异常。当引发了这个异常时,系统将定位到e x c e p t块的开头,并计算异常过滤器表达式的值,

5、过滤器表达式的结果值只能是下面三个标识符之一,这些标识符定义在 Wi n d o w s的E x c p t.h文件中(见表2 4-1)。表24-1 标识符及其定义标识符定义 为E X C E P T I O N _ E X E C U T E _ H A N D L E R1E X C E P T I O N _ C O N T I N U E _ S E A R C H0E 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-1第 24章 异常处理程序和软件异常计计579下载下面几节将讨论这些标识符如何改变线程的执行。在阅读这些内容时

6、可参阅图 2 4-1,该图概括了系统如何处理一个异常的情况。图24-1 系统如何处理一个异常24.2 EXCEPTION_EXECUTE_HANDLER在F u n c m e i s t e r 2中,异常过滤器表达式的值是 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块中代码(异常处理程序代码)的跳转。在 e x c

7、 e p t块中代码执行完之后,系统考虑这个要被处理的异常并允许应用程序继续执行。这种机制使Wi n d o w s应用程序可以抓住错误并处理错误,再使程序继续运行,不需要用户知道错误的发生。但是,当e x c e p t块执行后,代码将从何处恢复执行?稍加思索,我们就可以想到几种可能性。580计计第五部分 结构化异常处理下载开始否是是否执行一个CPU 指令系统确定最里层的try 块这个t r y块是否有一个e x c e p t块过滤器表达式的值是什么?异常继续查找异常继续执行异常执行处理程序全局展开开始执行e x c e p t 块中的代码在e x c e p t块之后执行继续找到前面的最

8、内层t r y块是否有一个异常被引发?第一种可能性是从产生异常的 C P U指令之后恢复执行。在 F u n c m e i s t e r 2中执行将从对d w Te m p加1 0的指令开始恢复。这看起来像是合理的做法,但实际上,很多程序的编写方式使得当前面的指令出错时,后续的指令不能够继续成功地执行。在F u n c m e i s t e r 2中,代码可以继续正常执行,但是,F u n c m e i s t e r 2已不是正常的情况。代码应该尽可能地结构化,这样,在产生异常的指令之后的C P U指令有望获得有效的返回值。例如,可能有一个指令分配内存,后面一系列指令要执行对该内存的

9、操作。如果内存不能够被分配,则所有后续的指令都将失败,上面这个程序重复地产生异常。这里是另外一个例子,说明为什么在一个失败的 C P U指令之后,执行不能够继续。我们用下面的程序行来替代F u n c m e i s t e r 2中产生异常的C语句:对上面的程序行,编译程序产生C P U指令来执行除法,将结果压入栈中,并调用 m a l l o c函数。如果除法失败,代码就不能继续执行。系统必须向栈中压东西,否则,栈就被破坏了。所幸的是,微软没有让系统从产生异常的指令之后恢复指令的执行。这种决策使我们免于面对上面的问题。第二种可能性是从产生异常的指令恢复执行。这是很有意思的可能性。如果在 e

10、 x c e p t块中有这样的语句会怎么样呢:在e x c e p t块中有了这个赋值语句,可以从产生异常的指令恢复执行。这一次,将用 2来除5,执行将继续,不会产生其他的异常。可以做些修改,让系统重新执行产生异常的指令。你会发现这种方法将导致某些微妙的行为。我们将在“EXCEPTION_ CONTINUE_EXECUTION”一节中讨论这种技术。第三种可能性是从e x c e p t块之后的第一条指令开始恢复执行。这实际是当异常过滤器表达式的值为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块中的代码

11、结束执行后,控制从e x c e p t块之后的第一条指令恢复。24.2.1 一些有用的例子假如要实现一个完全强壮的应用程序,该程序需要每周 7天,每天2 4小时运行。在今天的世界里,软件变得这么复杂,有那么多的变量和因子来影响程序的性能,笔者认为如果不用S E H,要实现完全强壮的应用程序简直是不可能的。我们先来看一个样板程序,即 C的运行时函数s t r c p y:这是一个相当简单的函数,它怎么会引起一个进程结束呢?如果调用者对这些参数中的某一个传递N U L L(或任何无效的地址),s t r c p y就引起一个存取异常,并且导致整个进程结束。使用S E H,就可以建立一个完全强壮的

12、s t r c p y函数:第 24章 异常处理程序和软件异常计计581下载这个函数所做的一切就是将对s t r c p y的调用置于一个结构化的异常处理框架中。如果 s t r c p y执行成功,函数就返回。如果 s t r c p y引起一个存取异常,异常过滤器返回E X C E P T I O N _E X E C U T E _ H A N D L E R,导致该线程执行异常处理程序代码。在这个函数中,处理程序代码什么也不做,R o b u s t S t r C p y只是返回到它的调用者,根本不会造成进程结束。我们再看另外一个例子。这个函数返回一个字符串里的以空格分界的符号个数:

13、这个函数分配一个临时缓冲区并将一个字符串复制到里面。然后该函数用C运行时函数strtok来获取字符串的符号。临时缓冲区是必要的,因strtok要修改它所操作的串。感谢有了SEH,这个非常简单的函数就处理了所有的可能性。我们来看在几个不同的情况下函数是如何执行的。首先,如果调用者向函数传递了N U L L(或任何无效的内存地址),n H o w M a n y To k e n s被初始化成-1。在t r y块中对s t r l e n的调用会引起存取异常。异常过滤器获得控制并将控制转移给e x c e p t块,e x c e p t块什么也不做。在e x c e p t块之后,调用f r e

14、 e来释放临时内存块。但是,这个内存从未分配,所以结束调用 f r e e,向它传递N U L L作为参数。ANSI C明确说明用N U L L作为参数调用f r e e是合法的。这时f r e e什么也不做,这并不是错误。最后,函数返回-1,指出失败。注意进程并没有结束。其次,调用者可能向函数传递了一个有效的地址,但对 m a l l o c的调用(在t r y块中)可能失败并返回 N U L L。这将导致对 s t r c p y的调用引起一个存取异常。同样,异常过滤器被调用,e x c e p t块执行(什么也不做),f r e e被调用,传递给它N U L L(什么也不做),返回-1,

15、告诉调用程序该函数失败。注意进程也没有结束。最后,假定调用者向函数传递了一个有效的地址,并且对 m a l l o c的调用也成功了。这种情况下,其余的代码也会成功地在n H o w M a n y To k e n s变量中计算符号的数量。在t r y块的结尾,异582计计第五部分 结构化异常处理下载常过滤器不会被求值,e x c e p t块中代码不会被执行,临时内存缓冲区将被释放,并向调用者返回n H o w M a n y To k e n s。使用S E H会感觉很好。R o b u s t H o w M a n y To k e n函数说明了如何在不使用t r y-f i n a

16、 l l y的情况下保证释放资源。在异常处理程序之后的代码也都能保证被执行(假定函数没有从 t r y块中返回应避免的事情)。我们再看一个特别有用的S E H例子。这里的函数重复一个内存块:这个函数分配一个内存缓冲区,并从源块向目的块复制字节。然后函数将复制的内存缓冲区的地址返回给调用程序(如果函数失败则返回N U L L)。希望调用程序在不需要缓冲区时释放它。这是在e x c e p t块中实际有代码的第一个例子。我们看一看这个函数在不同条件下是如何执行的。如果调用程序对 p b S r c参数传递了一个无效地址,或者如果对 m a l l o c的调用失败(返回N U L L),m e m

17、 c p y将引起一个存取异常。该存取异常执行过滤器,将控制转移到e x c e p t块。在e x c e p t块内,内存缓冲区被释放,p b D u p被设置成N U L L以便调用程序能够知道函数失败。这里,注意ANSI C允许对f r e e传递N U L L。如果调用程序给函数传递一个有效地址,并且如果对 m a l l o c的调用成功,则新分配内存块的地址返回给调用程序。24.2.2 全局展开当一个异常过滤器的值为E X C E P T I O N _ E X E C U T E _ H A N D L E R时,系统必须执行一个全局展开(global unwind)。这个全局

18、展开使所有那些在处理异常的 t r y _ e x c e p t块之后开始执行但未完成的 t r y-f i n a l l y块恢复执行。图 2 4-2是描述系统如何执行全局展开的流程图,在解释后面的例子时,请参阅这个图。第 24章 异常处理程序和软件异常计计583下载函数F u n c O S t i m p y 1和F u n c O r e n 1结合起来可以解释S E H最令人疑惑的方面。程序中注释的标号给出了执行的次序,我们现在开始做一些分析。F u n c OSt i m p y 1开始执行,进入它的t r y块并调用F u n c O R e n 1。F u n c O R

19、e n 1开始执行,进入它的t r y块并等待获得信标。当它得到信标,F u n c O R e n 1试图改变全局数据变量g _ d w P r o t e c t e d D a t a。但由于除以0而产生一个异常。系统因此取得控制,开始搜索一个与 e x c e p t块相配的t r y块。因为F u n c O R e n 1中的t r y与同一个f i n a l l y块相配,所以系统再上溯寻找另外的t r y块。这里,系统在F u n c O S t i m p y 1中找到一个t r y块,并且发现这个t r y块与一个e x c e p t块相配。系统现在计算与F u n c

20、 O S t i m p y 1中e x c e p t块相联的异常过滤器的值,并等待返回值。当系统看到返回值是E X C E P T I O N _ E X E C U T E _ H A N D L E R的,系统就在F u n c O R e n 1的f i n a l l y块中开始一个全局展开。注意这个展开是在系统执行 F u n c O S t i m p y 1的e x c e p t块中的代码之前发生的。对于一个全局展开,系统回到所有未完成的 t r y块的结尾,查找与f i n a l l y块相配的t r y块。在这里,系统发现的f i n a l l y块是F u n c

21、 O R e n 1中所包含的f i n a l l y块。当系统执行 F u n c O R e n 1的f i n a l l y块中的代码时,就可以清楚地看到S E H的作用了。F u n c O R e n 1释放信标,使另一个线程恢复执行。如果这个 f i n a l l y块中不包含R e l e a s e S e m a p h o r e的调用,则信标不会被释放。在f i n a l l y块中包含的代码执行完之后,系统继续上溯,查找需要执行的未完成 f i n a l l y块。在这个例子中已经没有这样的f i n a l l y块了。系统到达要处理异常的t r y-e x

22、 c e p t块就停止上溯。这时,全局展开结束,系统可以执行e x c e p t块中所包含的代码。结构化异常处理就是这样工作的。S E H比较难于理解,是因为在代码的执行当中与系统牵584计计第五部分 结构化异常处理下载扯太多。程序代码不再是从头到尾执行,系统使代码段按照它的规定次序执行。这种执行次序虽然复杂,但可以预料。按图2 4-1和图2 4-2的流程图去做,就可以有把握地使用S E H。图24-2 系统如何执行一个全局展开为了更好地理解这个执行次序,我们再从不同的角度来看发生的事情。当一个过滤器返回E X C E P T I O N _ E X E C U T E _ H A N D

23、 L E R时,过滤器是在告诉系统,线程的指令指针应该指向e x c e p t块中的代码。但这个指令指针在F u n c O R e n 1的t r y块里。回忆一下第2 3章,每当一个线程要从一个t r y-f i n a l l y块离开时,必须保证执行f i n a l l y块中的代码。在发生异常时,全局展开就是保证这条规则的机制。24.2.3 暂停全局展开通过在f i n a l l y块里放入一个r e t u r n语句,可以阻止系统去完成一个全局展开。请看下面的代码:第 24章 异常处理程序和软件异常计计585下载全局展开开始记住过滤器值为EXCEPTION_ EXECUTE

24、_HANDLER 的try块的地址系统确定最内层的try 块是否是否执行e x c e p t块中的处理程序代码是否到达处理异常的t r y块?是否到达处理例外的t r y块?找到前面的最内层try 块执行finally 块中的代码在F u n c P h e a s a n t的t r y块中,当调用s t r c p y函数时,会引发一个内存存取异常。当异常发生时,系统开始查看是否有一个过滤器可以处理这个异常。系统会发现在 F u n c M o n k e y中的异常过滤器是处理这个异常的,并且系统开始一个全局展开。全局展开启动,先执行F u n c P h e a s a n t的f

25、i n a l l y块中的代码。这个代码块包含一个r e t u r n语句。这个r e t u r n语句使系统停止做展开,F u n c P h e a s a n t将实际返回到F u n c F i s h。然后F u n c F i s h又返回到函数F u n c M o n k e y。F u n c M o n k e y中的代码继续执行,调用M e s s a g e B o x。注意F u n c M o n k e y的异常块中的代码从不会执行对 M e s s a g e B e e p的调用。F u n c P h e a s a n t的f i n a l l y

26、块中的r e t u r n语句使系统完全停止了展开,继续执行,就像什么也没有发生。微软专门设计S E H按这种方式工作。程序员有可能希望使展开停止,让代码继续执行下去。这种方法为程序员提供了一种手段。原则上,应该小心避免在 f i n a l l y块中安排r e t u r n语句。24.3 EXCEPTION_CONTINUE_EXECUTION我们再仔细考察一下异常过滤器,看它是如何计算出定义在 E x c p t.h中的三个异常标识符之一的。在“F u n c m e i s t e r 2”一节中,为简单起见,在过滤器里直接硬编码了标识符E X C E P T I O N _E X

27、 E C U T E _ H A N D L E R,但实际上可以让过滤器调用一个函数确定应该返回哪一个标识符。这里是另外一个例子。586计计第五部分 结构化异常处理下载这里,首先遇到的问题是在我们试图向 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

28、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

29、 _ 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块中的代码。这会显示一个消息框,用文本串报告发生了异常。如你所见,在异常过滤器中可以做很多的事情。当然过滤器必须返回三个异常标识符之一,但可以执行任何其他你想执行的任务。使用带警告的E X C E P T I O N

30、_ C O N T I N U E _ E X E C U T I O N我们已经知道,修改 F u n c l i n R o o s e v e l t 1函数中的问题可使系统继续执行,也可能不执行,这要取决于程序的目标C P U,取决于编译程序为CC+语句生成的指令,取决于编译程序的选项设置。一个编译程序对下面的C/C+语句可能生成两条机器指令:机器指令可能是这个样子:第二条指令产生异常。异常过滤器可以捕获这个异常,修改 p c h B u ff e r的值,并告诉系统重新执行第二条C P U指令。但问题是,寄存器的值可能不改变,不能反映装入到p c h B u ff e r的新值,这样

31、重新执行C P U指令又产生另一个异常。这就发生了死循环。如果编译程序优化了代码,继续执行可能顺利;如果编译程序没有优化代码,继续执行就可能失败。这可能是个非常难修复的 b u g,需要检查源代码生成的汇编语言程序,确定程序出了什么错。这个例子的寓意就是在异常过滤器返回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每次总能成功:当离散地向一个保留区域提交存储区时。在第1 5章讨论过如何保存

32、一个大的地址空间,并向这个地址空间离散地提交存储区。V M A l l o c示例程序说明了这个例子。编写V M A l l o c程序的一种更好的办法是必要时使用S E H提交存储区,而不是每次调用Virtual Alloc函数。在第1 6章,我们讨论了线程栈。特别是,我们讲解了系统如何为线程的栈保留一个 1 M B的地址空间范围,以及在线程需要内存区时,系统如何自动向栈提交新的内存区。为此,系统在第 24章 异常处理程序和软件异常计计587下载内部建立了一个S E H框架。当一个线程试图去存取并不存在的栈存储区时,就产生一个异常。系统的异常过滤器可以确定这个异常是源于试图存取栈的保留地址空

33、间。异常过滤器调用Vi r t u a l A l l o c向线程的栈提交更多的存储区,然后过滤器返回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指令可以成功执行,线程可以继续运行。将虚拟内存技术同结构化异常处理结合起来,可以编写一些执行非常快,非常高效的程序。下一章的S p r e a d S h e e t示例程序将说明如何使用S E H有效地实现内存管理。这个代码执行得非常快。24.4 EXCEPTION_CONTINUE_SEARCH迄今为止我们看到的例子都很平常。通过增加一个函数调用

34、,让我们来看看其他方面的问题:当F u n c l i n R o o s e v e l t 2执行时,它调用F u n c A t u d e 2并传递参数N U L L。当F u n c A t u d e 2执行时,引发了一个异常。同前面的例子一样,系统计算与最近执行的 t r y块相关联的异常过滤器的值。在这个例子中,F u n c l i n R o o s e v e l t 2中的t r y块是最近执行的t r y块,所以系统调用O i l F i l t e r 2函数来求异常过滤器的值尽管这个异常是在F u n c l i n A t u d e 2函数中产生的。现在我们让

35、问题变得更复杂一点,在程序中再增加一个t r y _ e x c e p t块。588计计第五部分 结构化异常处理下载现在,当 FuncAtude 3试图向地址 N U L L里存放 0时,会引发一个异常。但这时将执行F u n c A t u d e 3 的异常过滤器。F u n c A t u d e 3的异常过滤器很简单,只是取值E X C E P T I O N _CONTINUE_ SEARCH。这个标识符是告诉系统去查找前面与一个 e x c e p t块相匹配的t r y块,并调用这个t r y块的异常处理器。因为F u n c A t u d e 3的过滤器的值为E X C E

36、 P T I O N _ C O N T I N U E _ S E A R C H,系统将查找前面的t r y块(在F u n c l i n R o o s e v e l t 3里),并计算其异常过滤器的值,这里异常过滤器是 O i l F i l t e r 3。O i l F i l t e r 3看到p c h B u ff e r是N U L L,将p c h B u ff e r设定为指向全局缓冲区,然后告诉系统恢复执行产生异常的指令。这将使 F u n c A t u d e 3的t r y块中的代码执行,但不幸的是,F u n c A t u d e 3的局部变量s z没有

37、变化,恢复执行失败的指令只是产生另一个异常。这样,又造成死循环。前面说过,系统要查找最近执行的与 e x c e p t块相匹配的t r y块,并计算它的过滤器值。这就是说,系统在查找过程当中,将略过那些与 f i n a l l y块相匹配而不是与 e x c e p t块相匹配的t r y块。这样做的理由很明显:f i n a l l y块没有异常过滤器,系统没有什么要计算的。如果前面例子中F u n c A t u d e 3包含一个f i n a l l y块而不是e x c e p t块,系统将在一开始就通过 F u n c l i n R o o s e v e l t 3的O i

38、 l F i l t e r 3计算异常过滤器的值。第2 5章提供有关E X C E P T I O N _ C O N T I N U E _ S E A R C H的更多信息。24.5 GetExceptionCode一个异常过滤器在确定要返回什么值之前,必须分析具体情况。例如,异常处理程序可能知道发生了除以0引起的异常时该怎么做,但是不知道该如何处理一个内存存取异常。异常过滤器负责检查实际情况并返回适当的值。下面的代码举例说明了一种方法,指出所发生异常的类别:第 24章 异常处理程序和软件异常计计589下载内部函数G e t E x c e p t i o n C o d e返回一个值,

39、该值指出所发生异常的种类:下面列出所有预定义的异常和相应的含意,这些内容取自 Platform SDK文档。这些异常标识符可以在Wi n B a s e.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

40、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

41、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

42、 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。一个异常过

43、滤器对一个不能继续的异常返回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来除一个整数 EXCEP

44、TION_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来除一个浮点。590计计第五部分 结构化异常处理下载 EXCEPTION_FLT _ I N E X A C T _ R E S U LT。浮点操作的结果不能精确表示成十进制小数。EXCEPTIO

45、N_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。浮点操作的结果小于允许的值。内部函数G e t E x c e p t i o n C o d e只能在一个过滤器中调用(-e x c e p t之后的括号里),或在一个异常处理程序中被调用。下面的代码是合法的:但是

46、,不能在一个异常过滤器函数里面调用 G e t E x c e p t i o n C o d e。编译程序会捕捉这样的错误。当编译下面的代码时,将产生编译错。可以按下面的形式改写代码:第 24章 异常处理程序和软件异常计计591下载异常代码遵循在 Wi n E r r o r.h文件中定义的有关错误代码的规则。每个 D W O R D被划分如表2 4-2所示。表24-2 一个错误代码的构成位3 1-3 02 92 82 7-1 61 5-0内容严重性系数微软/客户保留设备代码异常代码意义0成功0=微软定义必须为0微软定义微软客户定义1信息的代码(见表2 4-3)2警告1=客户定义3错误的代码

47、目前,微软定义了下面一些设备代码(见表2 4-3)。表24-3 设备代码及其值设 备 代 码值设备代码值FA C I L I T Y _ N U L L0FA C I L I T Y _ C O N T R O L1 0FA C I L I T Y _ R P C1FA C I L I T Y _ C E RT11FA C I L I T Y _ D I S PAT C H2FA C I L I T Y _ I N T E R N E T1 2FA C I L I T Y _ S TO R A G E3FA C I L I T Y _ M E D I A S E RV E R1 3FA C I

48、 L I T Y _ I T F4FA C I L I T Y _ M S M Q1 4FA C I L I T Y _ W I N 3 27FA C I L I T Y _ S E T U PA P I1 5FA C I L I T Y _ W I N D O W S8FA C I L I T Y _ S C A R D1 6FA C I L I T Y _ S E C U R I T Y9FA C I L I T Y _ C O M P L U S1 7我们将E X C E P T I O N _ A C C E S S _ V I O L AT I O N异常代码拆开来,看各位(b i

49、t)都是什么。在Wi n B a s e.h中找到E X C E P T I O N _ A C C E S S _ V I O L AT I O N,它的值为0 x C 0 0 0 0 0 0 5:C 0 0 0 0 0 0 5 (十六进制)1100 0000 0000 0000 0000 0000 0000 0101 (二进制)第3 0位和第3 1位都是1,表示存取异常是一个错误(线程不能继续运行)。第2 9位是0,表示M i c r o s o f t已经定义了这个代码。第2 8位是 0,留待后用。第 1 6位至 2 7位是 0,代表FA C I L I T Y _ N U L L(存取

50、异常可发生在系统中任何地方,不是使用特定设备才发生的异常)。第0位到第1 5位包含一个数5,表示微软将存取异常这种异常的代码定义成5。24.6 GetExceptionInformation 当一个异常发生时,操作系统要向引起异常的线程的栈里压入三个结构,这三个结构是:592计计第五部分 结构化异常处理下载E X C E P T I O N _ R E C O R D结构、C O N T E X T结构和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结构包含有关已发生异常的独立于C P U的信息,C O

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 技术资料 > 其他杂项

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com