首页 > 代码库 > Windows 异常
Windows 异常
写在前面的话:
这里的标题是Windows 的异常,所以这里已明确告知,其实异常是一个系统行为,而我们以前经常听到的 C++ 异常,Java 异常,等等都是建立在这基础之上,重新形成的自己的框架,所以 Windows 的异常是它们的基础。
异常的发生:
在程序编写过程中,异常会时有发生,比如常见的遇到了除 0 异常,空指针异常,野指针异常等等。这些异常大家可能都耳熟能详,除 0 异常很简单,数学上除 0 是没法运算的,计算机中同理。后两种异常可能大家平时都只知其表,我们可以简单的理解为,空指针异常是因为地址为空的内存是保留内存,专门为程序用来进行指针初始化使用的,当指针初始化为 NULL时,表明这个指针不代表任何东西,且不能使用。野指针异常则是,指针指向了一段不能使用的内存,产生了访问违例。
异常的处理:
当遇到前两种异常时,程序是很容易处理的,因为通过程序判断,除数为 0 或者指针为空,就可以把这些异常扼杀在萌芽之中,但是野指针异常却很难处理,如果遇到这个问题,我们通常的做法是,将这个异常捕获,并通知开发人员,进行分析野指针产生的原因,那么如果捕获这些异常呢?
异常的捕获:
早先在 Winodws 上采用的方法是结构化异常处理,也即 SEH 。就是我们经常采用的 __try, __except 等关键字来合包围的异常块和处理块,当 __try 块中的代码发生异常时,根据异常筛选,就会跳入到 __except 块。这种异常处理还是相当灵活的,它可以嵌套存在,即 __try 块中的代码还有可能调用到含有另一个 __try 块的函数或代码。为了完成这样的功能,编译器其实巧妙地使用了栈来保留这些信息,简单点理解就是,当遇到 __try 块时,就在当时栈中记录下它的异常处理函数的地址,当函数再次遇到 __try 时,再次压栈,当 __try 块执行完毕时,从栈中弹出这个值,即,__try 块就相当于函数调用一般,这些信息完美地保存在了栈中,并且形成一个链表。那么当遇到一个异常,最后一次入栈的捕获的信息将会得到处理。如果它返回继续搜索,那么链表中的每个异常捕获的代码将会依次执行。
那么我们会看到它的几个特点:
1. 它的基于线程的,因为是在堆栈中保存的信息;
2. 它是基于 __try, __except 关键字来标识的;
3. 后写和 __try 永远会先执行。
看似完美的方案,其它是存在缺点的,假如我们想捕获到没有处理的异常,并输出日志,你可能会选择在最外层函数,有可能是 main 函数包含一个 __try 块,但如果你调用的代码中,有一段代码是第三方的,且它也有自己的 __try 块,并且发生异常时,它的处理异常的手段是,发生了异常就退出程序,那么,你的异常捕获代码永远也得不到执行。
所以在 XP 之后,Windows 又引入了另一种异常处理的方式,叫做 VEH, 即向量化异常捕获。
它是通过 APIAddVectoredExceptionHandler 来注册的,它也可以重复调用,且异常处理函数也将形成一个链表,链表头的异常处理代码也将会先执行。但它具有以下特点。
1. 它是基于进程的,也相当于全局的;
2. 它不需要 __try, __except 关键字来标识,通过函数调用;
3. 它会在SEH 机制之前先得到异常处理权。
基于这些特点,写出自己的异常捕获的库,我想也只是时间的问题了。还有一种叫做 VCH ,在 64 位系统上会有支持,可以自行查阅一下相关资料。
Windows 异常