Delphi中的回调函数

原创 2003年11月18日 10:02:00

Delphi中的回调函数



回调函数
    回调(CallBack)函数就是一种两个对象之间的通知方法。被通知者事先约定好发生某种情况时的处理函数,在该情况发生时发起通知者调用该处理函数以完成回调。举例来说,A是一个函数,B是一个定时器对象,A想在指定时间达到时接收到B的通知,执行步骤如下:
//以下为Delphi伪码
TProc = procedure ();//回调函数类型定义,全局函数指针
procedure A();//实际的回调函数

TTimer = class//定时器类
Private
  FpA: TProc;
Public
  procedure TTimer.SetCallBack(pA: TProc);
  procedure Proc();
  begin 
    if Assigned(FpA) then FpA();//约定的时间到了,回调A
  end;
end;

//执行过程
Var  B: TTimer;
begin
  …
  B.SetCallBack(A);//事先约定处理函数A
  …
  B.Proc();//某个时刻会发生该方法的调用,则回调A
  …
end;
    上文中的TProc是全局函数的定义方式,而全局函数被回调时只能使用全局变量和局部变量,而不能作为对象来调用形如Self.Call的方法,使用起来确实不方便;而且全局函数还会污染命名空间,实在不是个好习惯。如果我们想在B.SetCallBack()中传递一个对象的方法又怎么做呢?实现他并不难,主要是使用TObject.MethodAddress的方法来实现的。把函数A的定义去掉,改为类TA:
TA = class
Published//注意:必须是Published
  Procedure CallBack();
End;
//执行过程
Var  B: TTimer;  A: TA;
begin
  …
  B.SetCallBack(A.MethodAddress('CallBack'));//事先约定处理函数A.CallBack
  …
  B.Proc();//某个时刻会发生该方法的调用,则回调A
  …
end;
    这样就可以了。有一点请注意,在声明TA的方法CallBack时,必须声明为Published,否则就会发生错误,这又是为什么呢?原来TObject.MethodAddress方法只能取出Published型的方法,如果没有声明为Published,则A.MethodAddress('CallBack')会返回空,导致错误。另外请注意,使用这种方法是不能将回调函数声明为带有参数的,因为这种方式参数根本无法传递到回调函数中。
    有了如上两个缺点,即必须定义为Published类型的方法、不能传递参数,这样的限制也是很大的。还有另外一种定义回调函数的方式,他可以解决这个问题。那就是将TProc的定义改为:
    TProc = procedure () of Object;
    那么,该回调函数就能够支持任意对象方法了,调用B.SetCallBack时也不用再调用A.MethodAddress('CallBack')来获得回调函数地址了,而是改为B.SetCallBack(A.CallBack)。
    如上三种回调方式,各有利弊,需要根据实际需要来决定使用哪一种。

Windows API中的回调函数
    在Windows API中有一些函数使用回调函数,他的运行机制与上文提到的Delphi中的回调机制相似。例如CreateThread、SetWindowLong、SetWindowsHook等函数。对应的回调函数可以定义为如下形式:
    procedure ThreadFunction(Ptr: Pointer);stdcall;//线程函数
    function MessageCallBackFunc(Wnd: HWND; Msg, WParam, LParam: Longint): Longint;stdcall;//窗口消息处理回调函数

回调函数的线程模式
    前文提到的回调函数除了线程函数之外,回调者和被回调者都在同一个线程中,这就象管理者将自己的电话告诉了许多工作者,管理者每次都告诉所有工作者该干什么(循环处理),而且一直询问工作者是不是做完某件事了(约定事件发生),直到工作者回答是,工作者打电话(发起回调)给管理者,管理者在不挂电话的过程中将该工作记录在案(回调处理)。
    这个步骤实在是罗嗦。管理者会被累垮的,而且也没有效率,过程可以改成这样,管理者在开始时将自己的电话告诉所有工作者,之后他就可以去会见客户或者喝杯茶,当某个工作者完成了工作,给管理者打电话,之后由管理者将该工作记录在案。
    假设管理者给工作者的电话没电了或者忘记随身携带,那么事情就麻烦了,所以工作者都处于等待状态。情况还可以改变,那就是管理者告诉工作者邮箱地址,管理者在空闲时处理一下邮件,发现有工作者已经完成了他的工作,他就将该工作记录在案。
    如上就是回调函数的三种线程模式:
1、 发起回调者(类比工作者)和被回调者(类比管理者)在同一个线程。
2、 发起回调者和被回调者分属不同的线程,发起回调者在回调时必须等待被回调者处理完成才能返回。
3、 发起回调者和被回调者分属不同的线程,发起回调者在回调时不需要等待被回调者处理完成就可以返回。

作于2005312

delphi 回调函数例子 用函数过程作为参数

今天有个朋友问我怎么用函数或者过程作为函数的参数呢,我说网上有挺多的,然而他告诉我很多例子运行不起来,我搜了几个测试了下,不知道是不是我自己的软件版本的问题,运行不了,所以自己研究了下,把自己能运行的...
  • u014028956
  • u014028956
  • 2015年07月08日 23:28
  • 3565

delphi 回调函数与函数指针

1.回调函数其实就是通过函数指针进行调用函数的,具体使用例子如下        a)  定义回调函数类型   type THDFunction=function(k:i...
  • u010219209
  • u010219209
  • 2015年08月21日 12:15
  • 1010

delphi 中的函数指针 回调函数

delphi 中的函数指针 回调函数
  • rznice
  • rznice
  • 2015年01月27日 08:49
  • 2578

Delphi中制作DLL中回调函数的应用

  • 2007年01月20日 15:28
  • 22KB
  • 下载

等待函数(保持界面正常响应)

取自网络 //延时函数,msec 为毫秒 procedure Delay(msec: single); var   FirstTickCount: real; begin   if msec > ...
  • liushinhwa
  • liushinhwa
  • 2015年01月05日 17:17
  • 1331

Delphi写的等待进程运行结束函数

procedure TForm1.Button1Click(Sender: TObject);var sCommandLine: string; bCreateProcess: boolean; ...
  • aroc_lo
  • aroc_lo
  • 2009年11月26日 09:17
  • 605

Delphi中回调函数的使用

 我不善言表,只能用例子来说明,以下为一个例子:功能大体描述:Form1中有一个Edit和一个Button,当点击BUTTON时弹出FORM2,FORM2中也有一个EDIT和一个BUTTON,当点击F...
  • xiao_jun_0820
  • xiao_jun_0820
  • 2008年03月13日 13:44
  • 1328

Delphi回调函数的使用-例子

功能大体描述:Form1中有一个Edit和一个Button,当点击BUTTON时弹出FORM2,FORM2中也有一个EDIT和一个BUTTON,当点击FORM2中的BUTTON时,将FORM2中的ED...
  • Tercel99
  • Tercel99
  • 2009年09月30日 17:29
  • 2031

回调函数总结

1.基础知识   所谓回调,就是模块A要通过模块B的某个函数b()完成一定的功能,但是函数b()自己无法实现全部功能,需要反过头来调用模块A中的某个函数a()来完成,这个a()就是回调函数 ...
  • a568921915
  • a568921915
  • 2017年03月24日 16:01
  • 432

回调函数实例与分析

回调函数是一个很有用,也很重要的概念。当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数。回调函数在windows编程使用的场合很多,比如Hook回调函数:MouseProc,GetMsgP...
  • fsc2988877
  • fsc2988877
  • 2007年02月28日 19:07
  • 1207
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Delphi中的回调函数
举报原因:
原因补充:

(最多只允许输入30个字)