如何声明通用类型的事件指针? ( 积分: 100 )

  • 主题发起人 主题发起人 phynex
  • 开始时间 开始时间
P

phynex

Unregistered / Unconfirmed
GUEST, unregistred user!
DELPHI用Variant可以代表各种类型的变量,
那有没有一种机制,赋值时可赋各种事件指针?
如:
var
p:pointer;//或Tobject,倒底该如何赋值?
begin
p:= ADODataSet1.FieldByName('a').OnGetText;
p:= ADODataSet1.AfterScroll;
 
DELPHI用Variant可以代表各种类型的变量,
那有没有一种机制,赋值时可赋各种事件指针?
如:
var
p:pointer;//或Tobject,倒底该如何赋值?
begin
p:= ADODataSet1.FieldByName('a').OnGetText;
p:= ADODataSet1.AfterScroll;
 
p:= ◎ADODataSet1.FieldByName('a').OnGetText;
p:= ◎ADODataSet1.AfterScroll;
 
这样写:不过没那么通用
type
TEvent1=procedure(sender:TObject) of object;

var
onXX:TEvent1;
begin
onXX:=button1.OnClick;
onXX:=form1.onCreate;
你可自己定义各种各样参数的事件指针,通常为了开发控件中的事件才这样做。
例如:
type
TRptEvent = procedure (ErrID,ErrDesc) of object;
var
FonErr:TRptEvent;
begin
if Assigned(FonErr) then FonErrr(实参表);
如果把这个FonErr在类的published中声明,做成控件后,将出现在事件列表中。
 
我挺好奇,你为什么需要这么做?难道你是想做自动化测试吗?
 
to xfz8124,
Dataset过程处理中,往往要先屏蔽对象先前的事件,比如BeforePost,AfterScroll, 以及其它事件,这些事件类型可能不一致,原要若干个变量以保存它们,为了偷懒,就想到了这一招,以一个通用指针或对象来保存任意一种事件类型变量。
 
但是,这样,你用一个变量,也不能保存很多事件,除非你用数组
 
喔,我只想用一个指针来保存一个事件。

//除非你用数组
我想知道,用数组如何来实现呢?
 
Pointer就行了

var
P:Pointer
//指针,保存事件用的


procedure TForm1.Button1Click(Sender: TObject);

begin
P := Addr(Button2.OnClick);//保存Button2.OnClick到P
Button2.OnClick := Nil
//把事件指空
end;

//被保存的事件
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage('A');
end;

procedure TForm1.Button3Click(Sender: TObject);
var
LP:PInteger
//中间指针变量,什么类型指针无所谓了,就是要记录一个地址而已
begin
LP := @TMethod(Button2.OnClick);//把事件地址给中间指针变量
LP^ := Integer(P);//回复以前保存的指针到中间指针变量(就是事件,地址是相同的)
end;
 
Delphi的RTTI支持这种机制,就象楼上这位兄台的方式,不过有一点需要注意,RTTI针对Published的方法

比如说一个类
TTest = class
public
property OnClick;
published
property OnKeyPress;
end;

这个时候,OnClick的事件指针就不能在运行时动态赋值了,而OnKeyPress就可以,其他的和楼上的一样
 
现在我有个类似的问题:

在delphi2005中,定义类

TC=class
FTimer:TTimer;
procedure DoTimerWake(Sender: TObject);
end;

新建一个工程,声明并创建这个类的实例,让FTimer的事件指向DoTimerWake
FTimer.OnTimer:=FTimer.DoTimerOnWake;

或者在类中的构造函数中写上
 FTimer.OnTimer:=DoTimerOnWake; 
应该也是一样的,(只是为了接管这个事件)

修改工程代码:

begin
Application.Initialize;
if IsConsole then
TextTestRunner.RunRegisteredTests
else
GUITestRunner.RunRegisteredTests;
end.

其实这个是delphi2005自带的DUnit向导生成的代码。关键的问题在于,到OnTimer该执行的时间,DoTimerWake并没有被调用,奇怪的是在普通的工程里就没有问题了。

  请高手赐教,分析这种现象,小弟不胜感激~ ~

全部源码可在下面地址下载:    http://xiccc.ys168.com

有delphi2005的朋友可以Down下来看一看
 
对不起各位了,已经解决了,把过程说组大家供参阅:

第一个原因,是两段代码之间的差异,使得检测不到位,如下:
用例:以一段时间之后是否生成预期文件为检测目标
Demo:简单写了几句设置对象状态,调用对象功能的代码,一看生成了文件,就不知所措了

后来想到可能是与RTTI,事件,消息有关,就在Demo的
Sleep(3500)
前后分别加上
application.processmessages;
然后测试了几次,终于发现这样一个现象:

事件被触发时,如果没有得到处理,就会积压下来,直到Application.processmessages;或调用的过程退出,才开始处理事件函数。以前也遇到过类似情况--在事件里写:
labelCount.Caption:=IntToStr(StrToInt(labelCount.Caption)+1);
如果事件正在执行中,lblCount的Caption变化是不会在界面显示的,但是如果写上
application.Processmessages;
将会立刻见到效果,不信,可以写个循环100000次,改变Form1.caption的事件试一试。

问题虽然解决了,不过仍然希望高手--delphi的上忍--给解析一下这种现象,小弟感激只怕不尽~~~
 
wr960204的说法似乎可行,测试通过以后就放分。
 
wr960204:下列写法有什么问题?按下列顺序执行就有问题。
1)点击Button1;
2)点击Form;
3)点击Button1;此时出错,何解?

var
P: Pointer;

procedure TForm1.FormClick(Sender: TObject);
var
LP: PInteger
//中间指针变量,什么类型指针无所谓了,就是要记录一个地址而已
begin
LP := @TMethod(Button1.OnClick)
//把事件地址给中间指针变量
LP^ := Integer(P)
//回复以前保存的指针到中间指针变量(就是事件,地址是相同的)

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('a');
P := Addr(Button1.OnClick)
//保存Button2.OnClick到P
Button1.OnClick := nil
//把事件指空
end;
 
注意我/的代码.你不要在Button1的事件里面处理自己的事件好不好.晕
 
给个理由?出错之前对Button1的事件赋值了!
 
显示不显示还有另外一个问题,就是说值已经改变了,但是Label的WM_PAINT消息还没有收到或者还没有处理,所以界面上还没有重画。Application.processmessages按照字面理解就是处理消息了,看看这个解释能接受不
 
Delphi本来就有这种东西的, 就是TMethod类型, 可以保存任意方法(当然也包括事件)
只是使用起来比较麻烦,必须将它转成特定类型的方法类型才能进行调用(那是当然的,因为编译器要根据方法的参数个数和类型才能生成代码),除非用汇编自己实现。
使用例子:
var
// 假设用event1用来保存button1的OnClick, event2保存button1.OnKeyDown
Event1, Event2: TMethod


...
// 保存
TNotifyEvent(Event1) := Button1Click;
TKeyEvent(Event2) := Button1KeyDown;
// 或者
Event1.Code := @TForm1.Button1Click;
Event1.Data := Form1;
Event2.Code := @TForm1.Button1KeyDown;
Event2.Data := Form1;

// 使用
TNotifyEvent(Event1)(Sender);
TKeyEvent(Event2)(Sender, Key, Shift);
// 或者:
asm
MOV EAX, Event1.Data
MOV EBX, Event1.Code
MOV EDX, Sender
CALL [EBX] // 调用Event1
end;

asm
MOV EAX, Event2.Data
MOV EDX, Sender
LEA ECX, Key
MOV EBX, Shift
PUSH EBX
MOV EBX, Event2.Code
CALL [EBX]
end
// 调用Event2
 
var
Event1, Event2: TMethod;
begin
if Assigned(Button1.OnClick) then
Event1.Code := @(button1.onclick)//todo:此处该如何写?
 
if assigned(Button1.OnClick) then
TNotifyEvent(Event1) := Button1.OnClick;
 
后退
顶部