Win32结构化异常处理(SEH)探秘(上)

在 Win32 操作系统提供的所有功能中,使用最广泛但最缺乏文档描述的也许就是结构化异常处理了(SEH),当你考虑 Win32 结构化异常处理时,你也许会想到诸如 _try,_finally 以及 _except 这些术语。你能在任何有关 Win32 的书中发现对 SEH 很好的描述(即使是 remedial)。即便是 Win32 SDK 也具备有相当完整的使用 _try,_finally 和 _except 进行结构化异常处理的概述。

有了这些文档,那为何还说 SEH 缺乏文档呢?其实,Win32 结构化异常处理是操作系统提供的一个服务。你能找到的关于 SEH 的所有文档都是描述特定编译器的运行时库,这个运行库对操作系统实现进行包装。_try,_finally 和 _except 这些关键字没有任何神奇的地方。微软的操作系统及其编译器系列定义这些关键字和用法。其他的编译器提供商则只是沿用这些语义。虽然借助编译器层的 SEH 可以挽回一些原始操作系统级 SEH 处理不良口碑,但在大众眼里对原始操作系统 SEH 细节的处理感觉依旧。

我收到人们大量的e-mail,都是想要实现编译器级的 SEH 处理,又无法找到操作系统功能提供的相关文档。通常我都是建议参考 Visual C++ 或者 Borland C++ 运行库源代码。唉,出于一些未知的原因,编译器级的 SEH 似乎是一个大的秘密,微软和 Borland 都不提供其对 SEH 支持的核心层源代码。

在本文中,我将一层一层对 SEH 进行解剖,以便展现其最基本的概念。我打算通过代码产生和运行时库支持将操作系统提供的功能和编译器提供的功能分开。当我深入代码考察关键的操作系统例程时,我将使用 Intel 平台上的 Windows NT4.0 作为基础。但我将要描述的大多数内容同样适用于其它处理器上运行的应用。

我打算避免涉及到真正的 C++ 异常处理,它们使用 catch(),而不是 _except。其实,真正的 C++ 异常处理实现非常类似于本文中描述的内容。但是 C++ 异常处理有一些额外的复杂性会影响我想要涉及的概念。

通过深入研究晦涩的 .H 和 .INC 文件来归纳 Win32 SEH 构成,我发现有一个信息源之一就是 IBM OS/2 头文件(尤其是 BSEXCPT.H)。为此你不要觉得大惊小怪。。此处描述的 SEH 机制在其源头被定义时,微软仍然开发 OS/2 平台(译注: OS/2 平台起初是IBM 和 微软共同研发的,后来由于种种原因两个公司没有再继续下去)。所以你会发现Win32 下的 SEH 和 OS/2 下的 SEH 极其相似。

SEH 浅析

从整体来看,SEH 的可谓不可一世,绝对压倒一切,我将从细微之处开始,用我自己的方式一层一层研究。如果你是一张白纸,以前从没接触过结构化异常处理,那就最好不过了。如果你以前使用过 SEH。那就尝试清理你头脑中的 _try,GetExceptionCode 和 EXCEPTION_EXECUTE_HANDLER 等诸如此类的词,权当自己是个新手。做一个深呼吸,准备好了吗?好,我们开始。

想象一下,我告诉你某个线程出错了,操作系统给你一个机会通知了这个线程错误,或者再具体一点,当线程出错后,操作系统调用某个用户定义的回调函数。这个回调函数可以所任何它想做的事情。例如,它可以修复任何原因导致的错误,或者播放一个 .wav 文件。不管回调函数做什么,其最后总是返回一个值,这个值告诉系统下一步做什么。(这里描述的情况不一定完全一样,但足够接近。)

假定当你的代码出现了混乱,你不得不回来,想看看回调函数是什么样子的?换句话说,你想知道什么样的异常信息呢?其实这无关紧要,因为 Win32 已经帮你决定了。一个异常回调函数就象下面这样:

EXCEPTION_DISPOSITION
__cdecl _except_handler(
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);

该原型出自标准的 Win32 头文件 EXCPT.H,初看就有那么一点不同凡响。如果你慢慢研究,其实并没有那么糟。例如,忽略返回类型(EXCEPTION_DISPOSITION)。基本上你看到的就是一个叫做 _except_handler  的函数,这个函数带有四个参数。

第一个参数是指向 EXCEPTION_RECORD 结构指针,该结构在 WINNT.H 中定义如下:

typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;

ExceptionCode 参数是由操作系统赋值给异常的一个数。你可以在 WINNT.H 文件中搜一下“STATUS_”开始的 #defines 内容便可以得到一系列不同的异常编码。例如 STATUS_ACCESS_VIOLATION 是大家再熟悉不过的异常编码了,其值是 0xC0000005。更复杂的异常编码可以从 Windows NT DDK 的 NTSTATUS.H 文件中找到。EXCEPTION_RECORD 结构中的第四个元素是异常发生的地址。剩下的 EXCEPTION_RECORD 域现在可以忽略,不用管它。

_except_handler 回调函数的第二个参数是指向建立者框架(establisher frame)结构的指针,在 SEH 中它是一个至关重要的参数,但现在可以不用关心它。

_except_handler 回调函数的第三个参数是 CONTEXT 结构的指针。CONTEXT 结构在 WINNT.H 中定义,它表示特定线程异常发生时寄存器的值:

typedef struct _CONTEXT
{
DWORD ContextFlags;
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
FLOATING_SAVE_AREA FloatSave;
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
DWORD Ebp;
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD Esp;
DWORD SegSs;
} CONTEXT;

时间: 2024-09-11 02:15:23

Win32结构化异常处理(SEH)探秘(上)的相关文章

Win32结构化异常处理(SEH)探秘(下)

展开 在挖掘展开(Unwinding)的实现代码之前让我们先来搞清楚它的意思.我在前面已经讲过所有可能的异常处理程序是如何被组织在一个由线程信息块的第一个DWORD(FS:[0])所指向的链表中的.由于针对某个特定异常的处理程序可能不在这个链表的开头,因此就需要从链表中依次移除实际处理异常的那个异常处理程序之前的所有异常处理程序. 正如你在Visual C++的__except_handler3函数中看到的那样,展开是由__global_unwind2这个运行时库(RTL)函数来完成的.这个函数

link中结构化异常处理方面的问题?如果我希望忽略异常请问怎么做?

问题描述 link中结构化异常处理方面的问题?如果我希望忽略异常请问怎么做? link中结构化异常处理方面的问题?如果我希望忽略异常请问怎么做? 解决方案 在lambda表达式内处理异常,不要丢出异常

数据无边界:非结构化数据在MaxCompute上的处理

这是DT(Data Technology)时代,每天有海量数据的加速产生,而每天产生的海量数据80%+是非结构化的,如何把握数据资源服务大众,激发生产力是每个互联网企业需要掌握的核心竞争力.我们的理想是MaxCompute在SQL线上实现与其它云数据(OSS, TableStore等) 的互联互通,用OSS(阿里云对外提供的海量.安全和高可靠的云存储服务)几种非结构化数据处理为范例,未来我们可以期待对各种非结构化数据的分布式处理成为可能,甚至开启气象数据.基因数据等多种大数据,建立与各种分布式系

细说windows的异常处理和实现——结构化异常

异常与C++的异常 C++编程中有一个重要的机制就是异常的处理,标准的C++定义了try...catch..throw的异常处理机制.但是这只是C++里的异常.   广义的异常可以分为硬件产生的异常和软件产生的异常,像throw这样的动作就是在软件上产生一个异常,但是像写入一个非法内存.除0这些都是硬件产生的异常: 异常的处理可以分为结构化异常处理(Structured Exception Handling)SEH和向量异常处理(Vectored exception handling)VEH  

ASP 中健壮的页结构的异常处理

异常处理     错误处理是让程序员牢骚满腹的东西之一.让我们来面对它,我们不写错误的代码就是了...或者类似的想法.不幸的是,代码中的运行时错误可能有许多的原因,从硬件.软件的改变到使用了别的开发团队的代码等等.有效的处理这些错误并使得它对于网站正常操作过程的中断最小化是每个有良知的程序员的责任.     在本文讨论的范围内,有三个不同的地方可以发生错误:脚本,中间件,以及IT内部架构.IT内部架构的错误,比方周期性的性能降低并导致IIS进行(Inetinfo.exe)崩溃几乎是无法避免的.这

ASP中健壮的页结构的异常处理

在本文讨论的范围内,有三个不同的地方可以发生错误:脚本,中间件,以及IT内部架构.IT内部架构的错误,比方周期性的性能降低并导致IIS进行(Inetinfo.exe)崩溃几乎是无法避免的.这种类型的错误通常只能打电话要求技术支持并且会让系统管理员忙上很久.开发者不能为阻止这类错误做些什么,但是我们通常能够应付和改正脚本和中间件中的砦蟆?在安装了IIS以后,缺省的服务器端脚本语言被设置成VBScript.许多Web 开发团队在他们的开发环境中保持了这些缺省设置,这是不幸的,因为VBScript对于

XHTML结构化:使用XHTML按标准重构网站

xhtml|标准 我们曾经为本节撰写的标题是:"XHTML:简单的规则,容易的方针."原因之一是,本节讨论的规则和方针是简单和容易的.原因之二是,一本简单和容易的WEB设计图书,就像超级市场的新式的免费商品一样,虽然常见却可以有效地吸引人的眼球,这样的东西可以刺激人的兴趣,并且鼓励人们尝试. 我确实希望本节的内容可以激发你的兴趣,并鼓励你去尝试.为什么这么说呢?因为一旦你掌握了本章包含的简单容易的理念,你就会重新思考网页运作的方式,并开始改变建造它们的方法.然而我并不希望你只是将代码重

MaxCompute上如何处理非结构化数据

0. 前言 MaxCompute作为阿里云大数据平台的核心计算组件,拥有强大的计算能力,能够调度大量的节点做并行计算,同时对分布式计算中的failover,重试等均有一套行之有效的处理管理机制. 而MaxCompute SQL能在简明的语义上实现各种数据处理逻辑,在集团内外更是广为应用,在其上实现与各种数据源的互通,对于打通整个阿里云的数据生态具有重要意义.基于这一点,最近MaxCompute团队依托MaxCompute2.0系统架构,引入了非结构化数据处理框架:通过外部表,为各种数据在MaxC

HTML结构化:DIV+CSS网页布局入门指南

css|网页  你正在学习CSS布局吗?是不是还不能完全掌握纯CSS布局?通常有两种情况阻碍你的学习: 第一种可能是你还没有理解CSS处理页面的原理.在你考虑你的页面整体表现效果前,你应当先考虑内容的语义和结构,然后再针对语义.结构添加CSS.这篇文章将告诉你应该怎样把HTML结构化. 另一种原因是你对那些非常熟悉的表现层属性(例如:cellpadding,.hspace.align="left"等等)束手无策,不知道该转换成对 应的什么CSS语句. 当你解决了第一种问题,知道了如何结