Delphi中的回调函数

本文主要介绍了Delphi中的回调函数,包括三种定义方式及各自利弊,还提及Windows API中部分函数使用的回调函数,其运行机制与Delphi相似。此外,阐述了回调函数的三种线程模式,即同线程、分属不同线程且需等待处理完成、分属不同线程无需等待处理完成。

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中制作DLL •一 Dll的制作一般分为以下几步: 1 在一个DLL工程里写一个过程或函数 2 写一个Exports关键字,在其下写过程的名称。不用写参数和调用后缀。 二参数传递 1 参数类型最好与window C++的参数类型一致。不要用DELPHI的数据类型。 2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。 3 用stdcall声明后缀。 4 最好大小写敏感。 5 无须用far调用后缀,那只是为了与windows 16位程序兼容。 三 DLL的初始化和退出清理[如果需要初始化和退出清理] 1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下: procedure DllEnterPoint(dwReason: DWORD);far;stdcall; dwReason参数有四种类型: DLL_PROCESS_ATTACH:进程进入时 DLL_PROCESS_DETACH进程退出时 DLL_THREAD_ATTACH 线程进入时 DLL_THREAD_DETACH 线程退出时 在初始化部分写: DLLProc := @DLLEnterPoint; DllEnterPoint(DLL_PROCESS_ATTACH); 2 如Form上有TdcomConnection组件,就Uses Activex,在初始化时写一句CoInitialize (nil); 3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。 四全局变量的使用 在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份, 但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据 传递,除非你用内存映像文件。 五、其他:调用方式按照标准的Windows调用方式. 六、关于参数传递 •Delphi程序之间调用DLL,如果要用String类型的话,要在引用的单元加上ShareMem 单元。 •如果Delphi写的DLL供其他开发工具使用的话,不要使用String类型,用PAnsiChar类型。 尽量使用标准DLL接口。指的是传递的参数类型及函数返回类型不能是Delphi特有的, 比如string(AnsiString),以及动态数组和含有这些类型成员的复合类型(如记录),也不 能是包含有这些类型成员数据成员的对象类型,以避免可能的错误。如果使用了string类型或 动态数组类型,且调用方不是Delphi程序,则基本上会报错。如果调用方是Delphi但调用方或 被调用方没有在工程文件的第一包含单元不是ShareMem,也可能会出错。 七、关于回调Funciton 你可以把Callback函数看作是一种特殊的消息响应函数,一般来说我们不会自己调用这种函数, 而是有某些系统函数调用,而且不需要向后传递消息。 只要象C/C++这样支持函数指针的语言都 有回调函数的概念,它实际上是向被调用函数传一个你的函数地址,然后被调用函数向通过你传 入的函数地址来调用你的函数 。 以上是结构化回调,到高级语言Object Pascal、C++中回调函数并没有退出,反而得到延伸与 扩展,在面向对像中的回调,其实是指面向对像中类对像的事件,事件就是原始的回调函数。面 向对像, 将回调函数定义成事件过程,在程序引用对像时,若指定了对像的过程事件后,那么在 要进行事件触发的地方检查事件过程是否分配,如果分中的就执行事,也就是执行了回调函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值