Delphi 异常机制与 SEH
书呆子
介绍 SEH 机制的文章很多,但基本都是 C++的,关于 Delphi 的很少.最近项目需要,仔细阅读了 VCL 关于异常的处理,有些心得体会,希望和大家一起分享.
SEH 简介
SEH (struct exception handling) 结构化异常处理是 WIN32 系统提供一种与语言无关的的异常 处理机制.编程语言通过对 SEH 的包装,使程序异常处理更加简单,代码结构更加清晰.常见的如,del phi 用到的 try exception end, try finally end,C++用到的_try{} _finally{} 和_try{} _exc ept {} 结构都是对 SEH 的包装. SEH 提供了两种方式供开发者使用,一种是线程级的,通过设置线程的 SEH 链表结构.线程的 TI B 信息保存在 FS:[0],而 TIB 的第一项就是指向 SEH 链表,所以,FS:[0]就是指向 SEH 链表,关于 S EH 结构后面介绍.第二种是进程级的,通过 API 函数 SetUnhandledExceptionFilter 设置过滤器函数 来获取异常,注意的是,这种方式只有在前面的异常机制都不予以处理的时候才会被触发. 关于更详细的 SEH 相关内容,请参见大牛 Matt Pietrek 的文章: A Crash Course on the Depths of Win32 Structured Exception Handling (原文) A Crash Course on the Depths of Win32 Structured Exception Handling (翻译) SEH 链表的结构如下:
Delphi 打造的最简单的 SEH 示例
program Project1;
{$APPTYPE CONSOLE}
uses SysUtils, Windows;
type PEXCEPTION_HANDLER = ^EXCEPTION_HANDLER;
PEXCEPTION_REGISTRATION = ^EXCEPTION_REGISTRATION; _EXCEPTION_REGISTRATION = record Prev: PEXCEPTION_REGISTRATION; Handler: PEXCEPTION_HANDLER; end;
EXCEPTION_REGISTRATION = _EXCEPTION_REGISTRATION;
_EXCEPTION_HANDLER = record ExceptionRecord: PExceptionRecord; SEH: PEXCEPTION_REGISTRATION; Context: PContext; DispatcherContext: Pointer; end;
EXCEPTION_HANDLER = _EXCEPTION_HANDLER;
const EXCEPTION_CONTINUE_EXECUTION = 0; ///恢复 CONTEXT 里的寄存器环境,继续执行 EXCEPTION_CONTINUE_SEARCH EXCEPTION_NESTED_EXCEPTION EXCEPTION_COLLIDED_UNWIND = 1; ///拒绝处理这个异常,请调用下个异常处理函数 = 2; ///函数中出发了新的异常 = 3; ///发生了嵌套展开操作
EH_NONE
= 0;
EH_NONCONTINUABLE EH_UNWINDING EH_EXIT_UNWIND EH_STACK_INVALID EH_NESTED_CALL
= 1; = 2; = 4; = 8; = 16;
STATUS_ACCESS_VIOLATION
= $C0000005; ///访问非法地址 = $C000008C; = $C000008D;
STATUS_ARRAY_BOUNDS_EXCEEDED STATUS_FLOAT_DENORMAL_OPERAND STATUS_FLOAT_DIVIDE_BY_ZERO STATUS_FLOAT_INEXACT_RESULT
= $C000008E; = $C000008F;
STATUS_FLOAT_INVALID_OPERATION = $C0000090; STATUS_FLOAT_OVERFLOW STATUS_FLOAT_STACK_CHECK STATUS_FLOAT_UNDERFLOW = $C0000091; = $C0000092; = $C0000093; = $C0000094; ///除 0 错误
STATUS_INTEGER_DIVIDE_BY_ZERO STATUS_INTEGER_OVERFLOW STATUS_PRIVILEGED_INSTRUCTION STATUS_STACK_OVERFLOW STATUS_CONTROL_C_EXIT
= $C0000095; = $C0000096;
= $C00000FD; = $C000013A;
var G_TEST: DWORD;
procedure Log(LogMsg: string); begin Writeln(LogMsg); end;
function ExceptionHan
dler(ExceptionHandler: EXCEPTION_HANDLER): LongInt; cdecl; begin Result := EXCEPTION_CONTINUE_SEARCH; if ExceptionHandler.ExceptionRecord.ExceptionFlags = EH_NONE then begin case ExceptionHandler.ExceptionRecord.ExceptionCode of STATUS_ACCESS_VIOLATION: begin Log('发现异常为非法内存访问,尝试修复 EBX,继续执行'); ExceptionHandler.Context.Ebx := DWORD(@G_TEST); Result := EXCEPTION_CONTINUE_EXECUTION; end; else Log('这个异常我无法处理,请让别人处理吧'); end;
end else if ExceptionHandler.ExceptionRecord.ExceptionFlags = EH_UNWINDING then Lo