WIN32汇编语言教程:第14章 异常处理 · 14.2 使用筛选器处理异常(1)

Windows下的异常处理可以有两种方式:筛选器异常处理和SEH异常处理。

筛选器异常处理的方式是由程序指定一个异常处理回调函数(在下面将统一简称为“回调函数”),当发生异常的时候,系统将调用这个回调函数,并根据回调函数的返回值决定如何进行下一步操作,这种方法和DOS系统中使用INT 24h中断来处理异常的方法是很像的。

在进程范围内,筛选器异常处理回调函数是惟一的,设置了一个新的回调函数后,原来的就失效了。

14.2.1 注册回调函数

可以使用SetUnhandledExceptionFilter函数来设置一个筛选器异常处理回调函数,准确地讲,这个回调函数不是替换了系统默认的异常处理程序,而是在它前面进行了一些预处理,操作的结果还是会被送到系统默认的异常处理程序中去,这个过程就相当于对异常进行一次“筛选”,这正是函数名中“Filter”一词的含义。

SetUnhandledExceptionFilter函数的使用方法是:

   invoke SetUnhandledExceptionFilter,offset _Handler

   mov lpPrevHandler,eax

函数的惟一参数是回调函数的地址,如果地址参数被指定为NULL的话,那么系统将去掉这个“筛子”而直接将异常送往系统默认的异常处理程序。函数的返回值是上一次设置的回调函数的入口地址,如果原来没有安装“筛子”,那么返回值将为NULL。

如果原来已经设置了一个回调函数的话,那么新的回调函数将换掉原来的回调函数,注意:不是在原先的回调函数前面再挂上一个新的回调函数。也就是说,当异常发生的时候,系统调用新的回调函数,在这个函数返回的时候并不会再去调用上一次设置的回调函数。一个形象的比喻就是Windows系统不会用两层筛子去筛东西。

本书所附光盘的Chapter14\TopHandler目录中有一个简单的筛选器异常处理的例子,其中的汇编源代码如下:

                  .386

                  .model flat,stdcall

                 option casemap:none

;####################################################################

include       windows.inc

include       user32.inc

includelib    user32.lib

include       kernel32.inc

includelib    kernel32.lib

;####################################################################

; 数据段

;####################################################################

                  .data

lpOldHandler      dd ?

 

                  .const

szMsg          db '异常发生位置:%08X,异常代码:%08X,标志:%08X',0

szSafe         db '回到了安全的地方!',0

szCaption      db '筛选器异常处理的例子',0

 

                 .code

;####################################################################

; Exception Handler 异常处理程序

;####################################################################

_Handler   proc      _lpExceptionPoint

                  local  @szBuffer[256]:byte

 

                  pushad

                  mov    esi,_lpExceptionPoint

                  assume esi:ptr EXCEPTION_POINTERS

                 mov    edi,[esi].ContextRecord

                 mov    esi,[esi].pExceptionRecord

                 assume esi:ptr EXCEPTION_RECORD,edi:ptr CONTEXT

                 invoke wsprintf,addr @szBuffer,addr szMsg,\

                              [edi].regEip,[esi].ExceptionCode,[esi].ExceptionFlags

                         invoke MessageBox,NULL,addr @szBuffer,NULL,MB_OK

                         mov    [edi].regEip,offset _SafePlace

                          assume esi:nothing,edi:nothing

                         popad

                          mov    eax,EXCEPTION_CONTINUE_EXECUTION

                          ret

 

_Handler   endp

;####################################################################

start:

                          invoke SetUnhandledExceptionFilter,addr _Handler

                         mov    lpOldHandler,eax

;********************************************************************

; 会引发异常的指令

;********************************************************************

                          xor    eax,eax

                          mov    dword ptr [eax],0 ;产生异常,然后_Handler被调用

;                      ...

; 如果这中间有指令,这些指令将不会被执行!

;                      ...

_SafePlace:

                          invoke  MessageBox,NULL,addr szSafe,addr szCaption,MB_OK

                         invoke SetUnhandledExceptionFilter,lpOldHandler

                          invoke ExitProcess,NULL

;####################################################################

                          end    start

程序的入口处使用SetUnhandledExceptionFilter函数将_Handler子程序指定为异常处理回调函数,函数返回的原回调函数地址被保存到lpOldHandler变量中(当然,在这个例子中,这个值肯定为0,程序中进行这一步操作是为了演示保存和恢复的方法),在程序退出之前会再次使用SetUnhandledExceptionFilter函数将这个地址设置回去。

在设置好回调函数后,程序人为地产生一个读异常:在xor eax,eax指令将eax清零以后,mov dword ptr [eax],0指令将导致读写0地址处的内存,而这是不允许的,所以这条指令执行时会产生一个异常,系统将捕获到它并调用程序设置的_Handler子程序来进行预处理。在_Handler子程序中,将跳过产生异常的指令并将程序转移到由_SafePlace标号指定的“安全”位置去执行。

14.2.2 异常处理回调函数

筛选器异常处理回调函数的格式如下所示:

_Handler   proc   l  pExceptionInfo

回调函数带一个参数,这个参数是个指针,指向一个包含所发生异常详细信息的EXCEPTION_POINTERS结构,结构定义如下:

EXCEPTION_POINTERS STRUCT

 pExceptionRecord DWORD     ?

 ContextRecord    DWORD     ?

EXCEPTION_POINTERS ENDS

要处理一个异常,必须详细了解这个异常的各种信息,EXCEPTION_POINTERS结构中包含的正是这些内容,其中的pExceptionRecord字段指向一个EXCEPTION_RECORD结构,这个结构中包含了异常产生的原因、产生的位置等情况,而ContextRecord字段指向一个CONTEXT结构,结构中记录了异常产生时刻的运行环境。这两个结构的定义在13.3.3小节中介绍调试API的时候介绍过。

上页:第14章 异常处理 · 14.1 异常处理的用途 下页:第14章 异常处理 · 14.2 使用筛选器处理异常(2)

第14章 异常处理

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号