beta 的第四篇心得:一个释放后自动清空实例指针的类 (20分)

  • 主题发起人 主题发起人 beta
  • 开始时间 开始时间
没办法,只好用内嵌汇编了。[:(]
function DarwinZhangAssigned(P:TObject):Boolean;
const
offset=4;
begin
Result:=False;
if FindHInstance(P)=0 then
exit;
{$IFDEF VER150} //7.0版本
if FindHInstance(PDWord( PDWord(P)^ -offset ))=0 then
exit;
if FindHInstance(Pointer(PDWord( PDWord(P)^ -offset )^))=0 then
exit;
Result:=True;
{$else
}
//其他版本情况
asm
mov edi,p
add edi,$08
db $3b,$3d,$18,$16,$45,$00
jle @@TrueResult
mov @result $0
jmp @@exit
@@TrueResult:
mov @Result,$1
@@exit:
end;
{$ENDIF}
end;
 
to beat: 你那里到底行不行?
 
to DarwinZhang:
我这里可以用。不过你的“db $3b,$3d,$18,$16,$45,$00”何解?
机器代码?怎么搞到的?From D7?
不过我始终觉得这个办法有点“那个”:)
别叫我 beat,听起来想 beast :)我是 beta
多谢了:)
 
现在又不行了,晕:(
主要是 Free 后,nil 前的测试,刚才还可以 'not Assigned' 的,现在
死活报 'Assigned' 了。
经试验,本来测试通过,在窗口上放一个 TEdit 就乱报。
有时还会因为多放的控件不同而出现不同的乱报:(
测试代码如下:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

TTest = class
end;

var
Form1: TForm1;
implementation
{$R *.dfm}
function DarwinZhangAssigned(P:TObject):Boolean;
const
offset=4;
begin
Result:=False;
if FindHInstance(P)=0 then
exit;
{$IFDEF VER150} //7.0版本
if FindHInstance(PDWord( PDWord(P)^ -offset ))=0 then
exit;
if FindHInstance(Pointer(PDWord( PDWord(P)^ -offset )^))=0 then
exit;
Result:=True;
{$else
}
//其他版本情况
asm
mov edi,p
add edi,$08
db $3b,$3d,$18,$16,$45,$00
jle @@TrueResult
mov @result,$0
jmp @@exit
@@TrueResult:
mov @Result,$1
@@exit:
end;
{$ENDIF}
end;

procedure TForm1.Button1Click(Sender: TObject);
var
a: TTest;
begin
a := TTest.Create();
if DarwinZhangAssigned(a) then
Memo1.Lines.Add('Assigned')
else
Memo1.Lines.Add('Not Assigned');
a.Free;
if DarwinZhangAssigned(a) then
Memo1.Lines.Add('Assigned')
else
Memo1.Lines.Add('Not Assigned');
a := nil;
if DarwinZhangAssigned(a) then
Memo1.Lines.Add('Assigned')
else
Memo1.Lines.Add('Not Assigned');
end;

end.

直接测试输出:
Assigned
Not Assigned
Not Assigned
若在窗口上加放一个 TEdit 或 TRadioBox 过后测试输出:
Not Assigned
Not Assigned
Not Assigned
若是加放一个 TButton 或 TMemo 后测试输出:
Assigned
Not Assigned
Not Assigned
:(
看来你这个函数还是有问题:(
 
to beta:
对不起,那个beat是着急,打错了。[:)]
唉,那个程序我是在同学那里试验的,没有好意思很详细的调试,其实这个东西有点勉强。
本来以为可以了,现在没办法[:(],只好去装一个Dephi6了。^_^
 
btw:
来自:beta, 时间:2003-1-3 1:08:00, ID:1556758
来自:beta, 时间:2003-1-3 14:24:00, ID:1558065
来自:beta, 时间:2003-1-14 23:15:00, ID:1580194
来自:beta, 时间:2003-2-14 20:49:00, ID:1622993
来自:beta, 时间:2003-2-21 4:56:00, ID:1635067
我从你的时间看简直是神出鬼没啊!^_^
 
//我从你的时间看简直是神出鬼没啊!^_^
呵呵,程序员都这样:)
 
这里有好东东请自已看吧
http://lui2008.8u8.com
 
to β 兄:
那个db $3b,$3d,$18,$16,$45,$00就是 cmp edi [curalloc],
我本来以为curalloc是个常量,昨天比较仔细的看了一下,发现并非如此。
原来Delphi是在编译的时候动态的决定的。
正如您所说的,在Delphi6下面要写XXXAssigned函数并非一件容易的事情。
我发现我对Free还真是了解不够啊!以后要加强了。
 
beta 兄原来最近一直昼伏夜出,经过 DarwinZhang 兄的总结使他从此无可遁形。:-)
关于对象的 Offset 值,在 32 位系统中,就是 4 。用代码可以看见:SizeOf(对象):integer 。
当一个对象存在,它在内存的地址也可以看见(就是这个 4 字节的具体数值):@对象:Pointer 。
同理,我们也可以得到对象的成员变量和成员函数的具体存放地址:Get对象:DWord 。下面代码
演示我的说明,其中成员函数,我直接获取窗口过程(窗口过程是一个特例,它是一个系统回调,
我只是利用它作为对象的成员的身份,以说明每个对象成员存放在不同的地址空间)。
var
Button:TButton;
Edit:TEdit;
begin
Button:=TButton.Create(self);
Edit:=TEdit.Create(self);
MessageBox(0,PChar(Format('ButtonSize:%d;EditSize:%d',[SizeOf(Button),SizeOf(Edit)])),
'Offset Value',MB_OK);
MessageBox(0,PChar(Format('PButton:%P;PEdit:%P',[@Button,@Edit])),'Location Value',MB_OK);
Button.Parent:=self;
Edit.Parent:=self;
MessageBox(0,PChar(Format('ButtonWndProc:%U;EditWndProc:%U',[GetWindowLong(Button.Handle,
GWL_WNDPROC),GetWindowLong(Edit.Handle,GWL_WNDPROC)])),
'WndProc Location Value',MB_OK);
FreeAndNil(Button);
FreeAndNil(Edit);
end;
到这里,我想说明的是对象的地址和它的成员并不一定是凑在一起的,但我们确实可以获得
他们的具体位置。接下来看一个夸张的效果:
var
Button:TButton;
Edit:TEdit;
begin
Button:=TButton.Create(self);
Edit:=TEdit.Create(self);
MessageBox(....);
// 同上, 下同
MessageBox(....);
Button.Parent:=self;
Edit.Parent:=self;
MessageBox(....);
ZeroMemory(@Button,4);
ZeroMemory(@Edit,4);
if Assigned(Button) then
MessageBox(0,'Botton Assigned','info',MB_OK)
else
MessageBox(0,'Botton Not Assigned','info',MB_OK);
end;
这时候,消息会告诉我们,Button 这个对象已经不存在了。但我们在实际的 Form 上,Button 照样存在。
假如,我们将 ZeroMemory 两句改为 Button.Free 和 Edit.Free 后,出现的情况是,消息告诉我们,这两
个对象依然存在(因为 Assigned 是判断给定地址的值是不是 #0),但 Form 上对象确确实实地消失了。
.........
 
DarwinZhang: [:)]
小雨哥:
您费那么大的劲证明了指针 Assigned 判断的是指针的内容是否为空。
其实我有更简单的证明方法:)
procedure TForm1.Button1Click(Sender: TObject);
var
Obj: Pointer;
begin
Obj := Button1;
if Assigned(Obj) then
// (*)
ShowMessage('Yes')
else
ShowMessage('No');
end;

直接观察 (*) 那一行的汇编代码:
test eax, eax
jz +$0b
看这个 test 就很明显了,Assigned 判断的就是指针是否为空(内容是否为 0)。
大家都知道对象的实例指针指向的是其虚方法表,Assigned 仅能判断该指针是否
悬空,而我们却希望它(或其代替品)能判断出它指向的空间是否已经释放。
DarwinZhang 兄刚才就是试图写出这样一个代替品,可惜暂时没有成功:(
而我的前面做法则是另一种思路:让指针指向的空间是否释放于该指针是否悬空
保持一致,于是就可以通过原版 Assigned 测试出我们期望的结果,虽说绕了个
弯子,至少在一定程度上能够实现:)
当然,如果能够找到直接的方法(若 DarwinZhang 兄研究成功)的话,就好了:)
期待 and 研究 中。。。。。。
 
to β 兄:
经过不懈之努力,最新成果,在我的Delphi6.0下详细验证后通过!^_^
但生成1000个TButton后又不行了.[:(]
只好来个非常规的。
//仅在我机器下的Delphi6.0下测试通过
function DarwinZhangAssigned(P:TObject):Boolean;
begin
Result:=False;
if FindHInstance(P)=0 then
exit;
if PInteger(P)^<$00400000 then
exit;
if PInteger(P)^>$00800000 then
exit;
Result:=True;
end;

惨了,惨了。[:D]
 
不行了,刚才亲自手工涂完了一个宣传版,手腕已经基本上废掉了[:(]
明天再试你的代码。[:D]
 
to DarwinZhang 兄:
老毛病,直接测试可以,在窗口上胡乱加几个控件就不行了[:(]
我最近比较忙,没有时间继续研究这个,暂时结贴,有机会再说。
DarwinZhang 兄要是研究出来了不妨开一新贴,另推荐 amingoo
大虾的一篇相关文章,暂时没有细看,恐怕对你有帮助:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1642063
 
to β 兄:
你在线吗?前面的更新了,最新的是不是就可以了?
我盯了许多情况的啊.其实
if PInteger(P)^<$00400000 then
exit;
还是有把握的,
这句:
if PInteger(P)^>$00800000 then
exit;
就没有把握了.靠的是实验.[:(]
 
一共只有 20 分,三十多人,乱分了,勿怪:)
 
还是不行,老毛病:(
 
后退
顶部