无符号整形 发表于 2016-7-4 11:19:58

未处理异常 2. 异常处理程序

本帖最后由 无符号整形 于 2016-7-4 21:41 编辑

上次说好了要交大家如何私底下处理异常,今天就教。在这篇文章结束时,我会和大家一起讨论如何改编错误的strcat程序。
今天主要讲:
1.__except标识符
2.EXCEPTION_EXECUTE_HANDLER
3.EXCEPTION_CONTINUE_EXECUTION
4.EXCEPTION_CONTINUE_SEARCH
5.升级版的strcat程序

好的,让我们进入正题吧!
1.__except标识符:
__except是一种标识符,当__try块在执行时抛出了异常(注意:它不会像finally块那样不让你退出)就会被执行,它比finally块稍微复杂一点点。因为windows要求__except块必须返回一个值,而且具体返回几是有要求的。
标识符格式:
__try
{
      //你认为很容易出错的代码
}
__except(/*处理的返回值,也可以理解成我们要求操作系统处理的方式*/)
{
      //异常处理程序
}
注意__except关键字,任何时候创建一个__try块,后面必须跟着一个__finally块或者__except块。但是try块后面不能同时有__finally块和__except块也不能有多个__finally块或__except块。但是他们互相可以嵌套
至于后面得参数就留到后面去解释啦!
2.EXCEPTION_EXECUTE_HANDLER
这个标识符用来决定except的处理方式
EXCEPTION_EXECUTE_HANDLER就等于告诉操作系统:“我知道这个错误(错误),并预计这个异常在某种情况下会发生,同时已经写了一些代码去来处理它,让这些代码现在就执行吧!“注意:这个是__except块后面跟的那个括号了面的值,在excpt.h中被定义为1。
EXCEPTION_EXECUTE_HANDLER的使用方法:
__try
{
      //你认为很容易出错的__代码
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
      //异常处理程序
}
好了,让我们试着改编上次的strcat程序吧!
上次的代码:
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<windows.h>
int main()
{
      __try{
                strcat(NULL, NULL);
      }
      __finally{
                MessageBox(NULL, "发生了未处理异常!", "错误", MB_OK | MB_ICONERROR);
      }
      return 0;
}
这次我们直接把__finally换成__except(EXCEPTION_EXECUTE_HANDLER),表示系统发现错误后立即执行__except'
也就是这样
#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<windows.h>
int main()
{
      __try{
                strcat(NULL, NULL);
      }
      __except (EXCEPTION_EXECUTE_HANDLER){
                MessageBox(NULL, "发生了未处理异常!", "错误", MB_OK | MB_ICONERROR);
      }
      return 0;
}
编译并运行,这下子不会弹出“xxx.exe已停止工作“的对话框了。嘿嘿,倒是弹出下面的对话框

耶!我们终于可以利用SEH私底下处理错误了!太好了!
这就说明了EXCEPTION_EXECUTE_HANDLER的含义,大家看完之后记得敲代码试试哦!
3.EXCEPTION_CONTINUE_EXECUTION
这个嘛,就是让系统吧出错的语句再执行一次,一般都会导致再次发生错误,然后再次执行而死循环!
不过在某些地方,还是有用的。
下面的代码摘自《Windows核心编程》:
TCHAR g_szBuffer;

void FunclinRoosevelt1() {
      int x = 0;
      TCHAR *pchBuffer = NULL;
      
      __try {
                *pchBuffer = TEXT('J');
                X = 5 / X;
      }

      __except(OilFilter1(&pchBuffer) ) {
                MessageBox(NULL, TEXT("An exception occurred") , NULL , MB_OK);
      }
      MessageBox(NULL,      TEXT("Function"), NULL, MB_OK);
}
LONG OilFilrer1(TCHAR **ppchBuffer){
      if(*ppchBuffer == NULL){
                *ppchBuffer = g_szBuffer;
                return (EXCEPTION_CONTIUE_EXECUTION);
      }
      return(EXCEPTION_EXECUTE_HANDLER);
}
《Windows核心编程》的解释如下
当函数将字符'J'至置于pchBuffer所指向的内存缓冲区内是,遇到了第一个问题:很不辛,我们没有初始化pchBuffer,让他指向全局内存缓冲区g_szBuffer。因此,pchBuffer的值为NULL.。CPU会抛出一个异常,并对发生异常的__try块所对应的__except块的异常过滤程序进行求值。后者则以变量pchBuffer作为参数值调用OilFilter1。

当OilFilter1开始执行,它首先检查**ppchBuffer的值是是不是NULL,如果是,设置**ppBuffer的值,让他指向全局内存缓冲g_szBuffer。预算这次异常处理返回结果为EXCEPTION_CONTIUE_EXECUTION。系统·在看到过滤程序的返回职为EXCEPTION_CONTIUE_EXECUTION后,将程序控制控制流跳转到导致异常的那条指令,并尝试再次执行这条指令。这次,指令执行成功,'J'被置于g_szBuffer所指向缓冲区的第一个字节。
代码继续执行,将会遇到0作为除数的异常。系统再一次计算异常过滤程序表达式。这一次,OilFilter检查到**ppchBuffer的值不是NULL,于是返回EXCEPTION_EXECUTE_HANDLER,让系统执行except代码块。于是一个消息框被弹出,指明一个错误发生。
4.EXCPTION_CONTIUE_SEARCH
这个就是让系统执行上次的__try块,一般很少用,了解即可。
实例:

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<windows.h>
int main()
{
        __try{
                strcat(NULL, NULL);
        }
        __except (EXCEPTION_CONTINUE_SEARCH){
                MessageBox(NULL, "发生了未处理异常!", "错误", MB_OK | MB_ICONERROR);
        }
        MessageBox(NULL, "我还好呢!", "提醒", MB_OK | MB_ICONEXCLAMATION);
        return 0;
}你猜会如何?就是延迟一会就停止工作






5.升级版strcat程序
我直接上代码看看大家看不看得懂

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<windows.h>
int main()
{
        __try{
                strcat(NULL, NULL);
        }
        __except (EXCEPTION_EXECUTE_HANDLER){
                MessageBox(NULL, "拼接异常!", "错误", MB_OK | MB_ICONERROR);
        }
        MessageBox(NULL, "我还好呢!", "提醒", MB_OK | MB_ICONEXCLAMATION);
        MessageBox(NULL, "拼接结果:NULLNULL", "结果出来了!", MB_OK | MB_ICONINFORMATION);
        return 0;
}
运行结果:





其他的就要你自己总结了哟!

黑龍 发表于 2016-8-4 07:43:59

__try __except无法在裸函数中使用= =如果我要在裸函数中设置异常处理怎么办?(SEH)

无符号整形 发表于 2016-8-4 08:14:07

黑龍 发表于 2016-8-4 07:43
__try __except无法在裸函数中使用= =如果我要在裸函数中设置异常处理怎么办?(SEH)

不太明白你的意思

黑龍 发表于 2016-8-4 09:50:26

无符号整形 发表于 2016-8-4 08:14
不太明白你的意思

就是说
__try和__except都不能在有__declspec(naked)的函数中,否则不能通过编译,就是说无法通过__try和__except实现异常处理。如果我要在有__declspec(naked)的函数下实现异常处理,该怎么办?

无符号整形 发表于 2016-8-4 13:39:17

黑龍 发表于 2016-8-4 09:50
就是说
__try和__except都不能在有__declspec(naked)的函数中,否则不能通过编译,就是说无法通过__try ...

先写个异常处理函数,然后再用call指令去调用它。

黑龍 发表于 2016-8-4 15:08:00

无符号整形 发表于 2016-8-4 13:39
先写个异常处理函数,然后再用call指令去调用它。

        push offset @_@
        push fs:
        mov fs:,esp
        mov eax,dr0;产生异常
        call FOd
@_@:
        pop fs:
        add esp,4

这样方便= =
页: [1]
查看完整版本: 未处理异常 2. 异常处理程序