分享
 
 
 

Delphi程序设计之--经验技巧

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

这些日子太忙了,今天把剩下的部分贴完,希望对大家有用。看过前一篇的都知道此文的作者和出处,我就不详细说了。

{ No. 16 }

//对于记录类型Record的分析。

实例:

type

TBaseRec = record

rStr: Integer;

rStr2: String;

rStr3: String;

end;

TStrRec = record

rStr: Integer;

rStr2: String;

rStr3: String;

rStr4: String;

end;

procedure TForm1.Button3Click(Sender: TObject);

var

vRec1: TStrRec;

vBaseRec: TBaseRec;

begin

vRec1.rStr := 1;

vRec1.rStr2 := '123123';

vRec1.rStr3 := '1';

vRec1.rStr4 := '1';

vBaseRec := TBaseRec(Pointer(@vRec1)^);

ShowMessage(IntToStr(vBaseRec.rStr) + '_' + vBaseRec.rStr2 + '_' + vBaseRec.rStr3);

//

end;

{说明:

1、记录类型互相转换时,必须保证基础Record类型,数据大小Sizeof应小于或等于扩展类型。保证转换后的记录类型对象的数据访问合法正确。

2、在Delphi中,使用记录类型互相转换最为平凡的就是在消息Record的实现上了。在Delphi中定义了若干于TMessage可同时描述消息接受信息的Record,如:

TWMKey = packed record

Msg: Cardinal;

CharCode: Word;

Unused: Word;

KeyData: Longint;

Result: Longint;

end;

当需要接受KeyDown和KeyUp的消息时,我们即可以使用TMessage也可以使用TWMKey作为消息接收的参数类型。因为Delphi为我们提供了若干便利的消息类型,所以我们在使用消息处理问世时就不会象VC中那样繁琐和易错了。

3、记录类型的使用还提供了一个不同语言间数据信息封装访问的途径。在不同语言间使用记录类型和记录类型指针时,应注意内部定义的变量的类型匹配问题。

记录类型的本质测试研究:

更改上面例子的实现部分,测试:

procedure TForm1.Button3Click(Sender: TObject);

var

vRec1: TStrRec;

rStr: Integer;

rStr2: String;

rStr3: String;

vpt: Integer;

begin

vRec1.rStr := 1;

vRec1.rStr2 := '123123';

vRec1.rStr3 := '1';

vRec1.rStr4 := '1';

vpt := Integer(@vRec1);

rStr := Integer(Pointer(vpt)^);

vPt := vPt + Sizeof(rStr);

rStr2 := String(Pointer(vpt)^);

vPt := vPt + Sizeof(rStr2);

rStr3 := String(Pointer(vpt)^);

ShowMessage(IntToStr(rStr) + '_' + rStr2 + '_' + rStr3);

end;

提示信息于开始例子相同,则推测:

1、Record类型中定义的数据是在一个连续空间中保存的

2、当定义函数时,如果考虑到函数处理的信息可能在后续版本中,需要扩充则可以使用记录变量的方式传递参数。当扩充函数时只需将记录变量根据此记录的版本号转换为对应的记录类型变量进行访问即可。具体实例可以参考Windows API函数的版本升级及扩展情况。

}

2003-6-17 19:52:00

2003-6-17 22:22:02 事件类型属性,通过属性赋值函数操作

{ No. 17 }

{在TypInfo单元中,有若干函数可以让我们操作Dephi管理的类的VMT。通过,属性名称和对象VTM直接访问或改变属性值。

公共的属性访问函数:GetPropValue;公共的属性设置函数:SetPropValue。其中,对事件属性信息的读取可以使用GetPropValue,但是却不能通过SetPropValue给事件属性赋值。

解决方案:使用SetMethodProp给控件属性赋值。procedure SetMethodProp(Instance: TObject; const PropName: string; const Value: TMethod); overload;其中,TMethod 用以描述操作函数。

TMethod = record

Code, //函数地址;可以通过类函数MethodAddress,取得函数地址。其中,只有声明在Published段的函数才能通过MethodAddress访问。

Data: Pointer; //对象地址

end;

}

//**************** 正常使用

type

TMyForm = class(TForm)

...

private

FMyText: String;

published

procedure MyClick(Sender: TObject);

end;

//窗体中,按钮事件;实现动态分配另一个按钮的事件的方法

procedure TMyForm.Button1OnClick(Sender: TObject);

var

vMethod: TMethod;

begin

FMyText := 'Hello Joy!';

vMethod.Code := Self.MethodAddress('MyClick'); //************ Code 1

vMethod.Data := Self; //************ Code 2

SetMethodProp(Button2, 'OnClick', vMethod);

end;

procedure TMyForm.MyClick(Sender: TObject);

begin

ShowMessage('Ok!'); //************ Show 1

ShowMessage(Self.FMyText); //************ Show 2

end;

//**************** 修改 一

//将[Code 1]和[Code 2]处的Self变更为TMyForm。则[Show 1]显示正常,[Show 2]显示不正常。

//说明:当类的函数被执行时,寄存器eax保存的是当前类的地址。所以,TMethod.Data中保存的应该是将来执行TMethod.Code函数时,赋给eax的值,即类对象指针。

//又因为[Show 1]中,不需要eax中类对象指针,所以可以正常执行。

//**************** 修改 二

//函数地址读取部分

procedure TMyForm.Button1OnClick(Sender: TObject);

var

vMethod: TMethod;

vEvent: TNotifyEvent;

begin

FMyText := 'Hello Joy!';

vEvent := MyClick;

vMethod.Code := @vEvent; //************ Code 1

vMethod.Data := Self; //************ Code 2

SetMethodProp(Button2, 'OnClick', vMethod);

end;

//其中,MyClick可以定义为私有函数。

//说明:vMethod只是要记录一个类的函数地址和类对象的地址。MethodAddress函数也只是通过函数名称进行函数地址的读取而已。

2003-6-18 12:48:38 不通过汇编访问 [VMT]

VMT:Virtual Method Table

//访问VMT信息

e.g.

procedure TForm1.Button1Click(Sender: TObject);

var

vpt: Pointer;

vMethod: TMethod;

begin

vMethod.Code := TForm.MethodAddress('MyClick');

vMethod.Data := Self;

if vMethod.Code = nil then ShowMessage('Error!');

vPt := Pointer(TListBox);

Integer(vPt) := Integer(vPt) + vmtTypeInfo; //在Delphi帮助中说明,根据偏移量可以取得ClassInfo。或者可以参考,TObject.ClassInfo的定义。

SetMethodProp(ListBox2, GetPropInfo(pTypeInfo(vPt^), 'OnClick'), vMethod);

end;

//其中,类的类到底如何提取?

function TObject.ClassType: TClass;

begin

mov eax, [eax]

end;

//上面的语句可以翻译成

function TObject.ClassType: TClass;

begin

Result := TClass(Pointer(Self)^);

end;

//所以上面的例子也可以改为

procedure TForm1.Button1Click(Sender: TObject);

var

vpt: Pointer;

vMethod: TMethod;

begin

vMethod.Code := TForm.MethodAddress('MyClick');

vMethod.Data := Self;

if vMethod.Code = nil then ShowMessage('Error!');

vPt := Pointer(Pointer(ListBox2)^); //取得ClassType的指针

Integer(vPt) := Integer(vPt) + vmtTypeInfo;

SetMethodProp(ListBox2, GetPropInfo(pTypeInfo(vPt^), 'OnClick'), vMethod);

end;

{仿照上面的例子,我们可以访问所有VMT入口地址,并取得相应的信息}

2003-6-21 11:24:32 使用汇编实现远程函数调用

{ No. 19 }

//如何通过指针,调用类函数中定义的函数(如下面的:MyFar)?

//如果我们只传递函数指针,然后调用函数的话,我们会发现,在MyFar中不能访问当前类对象的变量FMyText。如果嵌入一段汇编,将当前位置压入栈,然后再调用此函数,则就可以象类函数一样,在其中访问类的变量了。

e.g.

type

pMyRec = ^TMyRec;

TMyRec = record

rStr: String;

rInt: Integer;

end;

procedure MyProc(APro: Pointer);

var

CallerBp: Cardinal;

MyRec: TMyRec;

vPt: Pointer;

begin

MyRec.rStr := 'MyRec.rStr';

MyRec.rInt := 'MyRec.rInt';

vPt := Pointer(@MyRec);

asm

mov eax, [ebp]

mov CallerBp, eax

mov eax, vpt

Push CallerBp

end;

end;

procedure TMyTemp.SetText(aText: String);

procedure MyFar(aRec: TMyRec);

begin

ShowMessage(Format('%s_%sd_%s', [Self.FmyText, aRec.rStr, aRec.rInt]);

end;

begin

Self.FMyText := 'JoyYuan';

MyProc(Addr(MyFar));

end;

{说明:具体例子可以参考:Grids单元中,TSparsePointerArray.ForAll的实现。

}

2003-6-21 11:47:21 类函数地址

{ No. 20 }

测试结果列举:也是测试方法和测试思维经历的过程。

结果一:Button1.CanFocus与Button2.CanFocus的地址相同

结果二:Button1.CanFocus与Form1.CanFocus的地址相同,也等同于ListBox1.CanFocus

结果三:当在TForm1窗体类中,重载Form1.CanFocus后,Button1.CanFocus与Form1.CanFocus的地址不相同

结论:如果没有重载父类的虚函数,则访问时,直接得到并访问父类的函数。所以,TButton, TListBox, TForm默认都访问的是TWinControl的CanFocus。所以函数地址相同。

结果四:定义事件类型

Type

TMyEvent1 = Function(): Boolean of Object;

TMyEvent2 = function(): Boolean;

得结果:Sizeof(TMyEvent1) = 8; Sizeof(TMyEven2) = 4;

结论:类函数类型,保存的室两个指针的内容,见TMethod中,Code 和 Data;既一个函数指针,一个对象指针。

验证测试例子一:

var

vTestEvent: TNotifyEvent;

begin

Pointer((@@vTestEvent)^) := @TForm1.MyClick; //或者通过:TForm1.MethodAddress('MyClick') 方式取函数地址

Pointer(Pointer(Integer(@@vTestEvent) + 4)^) := Pointer(Self);

vTestEvent(nil); //效果和执行 Self.MyClick一样。

end;

验证测试例子二:

var

vMethod: TMethod;

begin

vMethod.Code := TForm1.MethodAddress('MyClick');

vMethod.Data := Self;

TNotifyEvent(vMethod)(nil);//效果和执行 Self.MyClick一样。

end;

文章作者:大富翁的joyyuan97

出处:http://www.delphibbs.com/keylife/iblog_show.asp?xid=1134

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有