开卷有益,这句话一点都不错,以前总以为自己对异常处理还是比较了解的,今天看了周爱民的《Delphi源代码分析》的错误和异常处理,对异常的处理又有了新的认识。这本书里很详细的讲述了系统和delphi里异常的处理。
每个例程的入口,常常能看到这样的代码:
xor eax,eax //eax清零
push ebp
push @@HandleInHere //(在栈上)形成异常帧,并插入到链表(FS:[0])头部
push dword ptr fs:[eax] //fs:[0] 系统默认的异常处理的链表首地址
发生异常的时候,系统去fs:[0]处查找异常链表,这是系统约定的。
原来系统在每个例程的入口,自动插入异常保护,如果用了try,首先把原有的异常处理函数地址入栈:push dword ptr fs:[eax],然后把新的异常处理函数地址赋给fs:[0] mov fs:[0], xxxx,异常处理完后,pop出原有的异常处理地址,也就是通过堆栈实现异常链。
在书里还学到了这么一段代码:
unit AssertLogs;
interface
implementation
uses
Windows,
SysUtils;
var
runErrMsg: string;
oldAssertErrorProc: TAssertErrorProc;
procedure LogAssert(const message, Filename: string; LineNumber: Integer;
ErrorAddr: Pointer);
begin
runErrMsg := Format('Error: %s, Addr: %p, in file(%d): %s',
[message, ErrorAddr, LineNumber, Filename]);
if IsConsole then
Writeln(runErrMsg)
else
MessageBox(0, pchar(runErrMsg), 'Error Log by AssertLogs', 0);
end;
initialization
oldAssertErrorProc := AssertErrorProc;
AssertErrorProc := @LogAssert;
finalization
AssertErrorProc := oldAssertErrorProc;
end.
这个单元有什么用呢?呵呵,妙不可言~~~~
在使用这个单元之前,我都是用Application.OnException来记录发生的错误,现在可以用Assert()了,试试效果吧。可以直接指出发生错误的单元文件和发生错误的行。