beta 的第六篇心得:如何判断程序刚才出现了异常(100分)

  • 主题发起人 主题发起人 beta
  • 开始时间 开始时间
楼上的兄弟您回帖之前至少应该先看一楼的内容吧:)
没想到这个帖子竟引来了 aimingoo 大虾
//的确不是这样,如果是try-finally-end块,则一旦发生异常,最先进入的并不是
//finally-end块,而是...
这我虽然知道,但是您更详细的讲解还是让我受益非浅:)
//这绝对是不安全的,eax的值并不能保证。不过有一点可以肯定,如果代码正常执行到
//finally-end,eax的确为0。
呵呵,从您后面的分析可以看出我的基于汇编代码流程的判断方式是可行的,当然,这
恐怕没有您的基于delphi异常机制的判断方式“保值”:)
//procedure _DoLog(EAX:DWORD);
哈哈,您这个方法完全违背了delp兄的本意,DoLog是好看了,却来了个不好看的“兄弟”
//function HaveException : boolean;
构思的确非常巧妙,为了取得栈中的返回地址,人为引发了个异常
不过经我测试无论如何都返回 False :(
环境:D6 + sp0 + Win2kPro
 
>>to zqw0117,
>>----------------
>>哎,你的建议其实有非常简单的解决方法,也是比较常用的。
>>{$IFOPT D+}
>>log_Exception....
>>{$else
}
>>do
your code....
>>{$END}
>>这就是编译调试版本和发布版本的不同之处。
多谢aimingoo大虾的指点!
 
to beta,
---------
我刚才完整地测试了在D6上的情况。下面是简单的测试代码:
program TestHaveException;
=========================================================
{$APPTYPE CONSOLE}
uses
SysUtils;
{$O-}
var
i : integer;
begin
try
i := 3;
i := 5 div i;
finally
Writeln(BoolToStr(HaveException, True));
end;

try
i := 0;
i := 5 div i;
finally
Writeln(BoolToStr(HaveException, True));
end;
end.
=========================================================
这里有一点是必须要说明的。
编译条件{$O-}不是必须的。但在这个例子中,却有用。由于
i := 0;
i := 5 div i;
这样的代码在执行后并没有用,所以优化时就给去掉了。因此,如果{$O+},那么无论如何都不会触发异常。其结果当然是两个False。
加上{$O-}就好了。
我查看过D2-D7的全部代码,异常的机制是一样的。因此上面这个例子是可以通用的。在实用中,用{$O+}也并没有关系,并不影响HaveException的有效性。
 
to beta,
---------
//procedure _DoLog(EAX:DWORD);
哈哈,您这个方法完全违背了delp兄的本意,DoLog是好看了,却来了个不好看的“兄弟”
其实这个只是个例子,说明可以这样用。在HaveException()中也有类似的的技巧。如果要干掉这个兄弟,其实也不复杂。做成内嵌函数就可以了:
proceduredo
Log;
procedure _DoLog(EAX:DWORD);
begin
// if you want, you cando
any... :)
end;
asm
call _DoLog
end;

当然,按你的原意可能更应该是这样(与是否Log无关):
function HaveException_beta : boolean;
asm
end;

其实这就已经通过EAX完成异常的检测了。我试过,Delphi正好也不会将这个函数优化掉。哈哈。
 
一下是我的测试代码,仍无法得到正确结果:(
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
implementation
{$R *.dfm}
function HaveException : boolean;
const
{$WRITEABLECONST ON}
IsFinally : DWORD = 0;
procedure GetFinallyReturnAddr;
begin
try
try
asm xor eax, eax;
idiv eax end;
finally
asm pop [IsFinally];
push [IsFinally] end;
end;
except
end;
end;

asm
CMP IsFinally, 0
JNE @@CHECK
CALL GetFinallyReturnAddr
@@CHECK:
MOV EAX, [ESP + 4]
CMP IsFinally, EAX
JE @@DONE
MOV EAX, 0
@@DONE:
end;

proceduredo
Log;
begin
if not HaveException then
ShowMessage('No Except')
else
ShowMessage('Any Except');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
i, j: Integer;
x:do
uble;
begin
i := 5;
j := StrToIntDef(Edit1.Text, 1);
try
x := i / j;
Caption := FloatToStr(x);
finally
do
Log;
end;
end;

end.
 
OH。哈哈,这样当然得不到结果。HaveException()检测在进入finally-end块时的状态,由于它检测的是堆栈状态,所以它不能在finally-end中的函数中被调用。你的DoLog却正好如此。
它的用法只能是:
============
finally
// 在之前的任何函数不会影响HaveException()的结果。
if HaveException then
do
Log();
else
do
ntLog()
end;

-------
这也表现出检测堆栈和检测eax的不同。如果检测eax,则在HaveException_beta()之前的任何代码都可能影响结果。而检测堆栈却不会。
 
哦,呵呵,差点忘了。要是在你的 HaveException 中的前面部分加上我的代码,
就可以实现在函数调用中判断了:)
 
多人接受答案了。
 
后退
顶部