使用回调函数(VC & Delphi)

宋博其

宋博其

2016-02-19 17:18

今天给大家分享的是由图老师小编精心为您推荐的使用回调函数(VC & Delphi),喜欢的朋友可以分享一下,也算是给小编一份支持,大家都不容易啊!

  回调函数是一个很有用,也很重要的概念。当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数。回调函数在windows编程使用的场合很多,比如Hook回调函数:MouseProc,GetMsgProc以及EnumWindows,DrawState的回调函数等等,还有很多系统级的回调过程。本文不准备介绍这些函数和过程,而是谈谈实现自己的回调函数的一些经验。

  之所以产生使用回调函数这个想法,是因为现在使用VC和Delphi混合编程,用VC写的一个DLL程序进行一些时间比较长的异步工作,工作完成之后,需要通知使用DLL的应用程序:某些事件已经完成,请处理事件的后续部分。开始想过使用同步对象,文件影射,消息等实现DLL函数到应用程序的通知,后来突然想到可不可以在应用程序端先写一个函数,等需要处理后续事宜的时候,在DLL里直接调用这个函数即可。于是就动手,写了个回调函数的原形。在VC和Delphi里都进行了测试。

  一:声明回调函数类型。

  vc版typedef int (WINAPI*PFCALLBACK)(intParam1,intParam2);

  Delph版 PFCALLBACK=function(Param1:integer;Param2:integer):integer;stdcall;

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)

  实际上是声明了一个返回值为int,传入参数为两个int的指向函数的指针。由于C++和PASCAL编译器对参数入栈和函数返回的处理有可能不一致,把函数类型用WINAPI(WINAPI宏展开就是__stdcall)或stdcall统一修饰。

  二:声明回调函数原形

  声明函数原形

  vc版 int WINAPICBFunc(intParam1,intParam2);

  Delphi版 function CBFunc(Param1,Param2:integer):integer;stdcall;

  以上函数为全局函数,如果要使用一个类里的函数作为回调函数原形,把该类函数声明为静态函数即可。

  三:回调函数调用调用者

  调用回调函数的函数我把它放到了DLL里,这是一个很简单的VC生成的WIN32DLL.并使用DEF文件输出其函数名TestCallBack。实现如下:

  

 PFCALLBACK gCallBack=0;
 void WINAPI TestCallBack(PFCALLBACKFunc)
 {
  if(Func==NULL) return;
  gCallBack=Func;
  DWORDThreadID=0;
  HANDLEhThread=CreateThread(NULL,NULL,Thread1,LPVOID(0),ThreadID);
  return;
 }

  此函数的工作把传入的PFCALLBACKFunc参数保存起来等待使用,并且启动一个线程。声明了一个函数指针PFCALLBACKgCallBack保存传入的函数地址。

  四:回调函数如何被使用:

  TestCallBack函数被调用后,启动了一个线程,作为演示,线程人为的进行了延时处理,并且把线程运行的过程打印在屏幕上.

  本段线程的代码也在DLL工程里实现

  

 ULONG WINAPI Thread1(LPVOID Param)
 {
  TCHAR Buffer[256];
  HDC hDC=GetDC(HWND_DESKTOP);
  int Step=1;
  MSG Msg;
  DWORD StartTick;
  //一个延时循环
  for(;Step200;Step++)
  {
   StartTick=GetTickCount();
   /*这一段为线程交出部分运行时间以让系统处理其他事务*/
   for(;GetTickCount()-StartTick10;)
   {
    if(PeekMessage(&Msg,NULL,0,0,PM_NOREMOVE))
    {
     TranslateMessage(&Msg);
     DispatchMessage(&Msg);
    }
   }
   /*把运行情况打印到桌面,这是vcbear调试程序时最喜欢干的事情*/
   sprintf(Buffer,"Running%04d",Step);
   if(hDC!=NULL)
    TextOut(hDC,30,50,Buffer,strlen(Buffer));
  }
  /*延时一段时间后调用回调函数*/ 
  (*gCallback)(Step,1);
  /*结束*/
  ::ReleaseDC(HWND_DESKTOP,hDC);
  return0;
 }

  五:万事具备

  使用vc和Delphi各建立了一个工程,编写回调函数的实现部分

  VC版

  

 int WINAPI CBFunc(int Param1,int Param2)
 {
  int res=Param1+Param2;
  TCHAR Buffer[256]="";
  sprintf(Buffer,"callbackresult=%d",res);
  MessageBox(NULL,Buffer,"Testing",MB_OK); //演示回调函数被调用
  return res;
 }
 

  Delphi版

  

 function CBFunc(Param1,Param2:integer):integer;
  begin
   result:=Param1+Param2;
   TForm1.Edit1.Text:=inttostr(result);//演示回调函数被调用
  end;

  使用静态连接的方法连接DLL里的出口函数TestCallBack,在工程里添加Button(对于Delphi的工程,还需要在Form1上放一个Edit控件,默认名为Edit1)。响应ButtonClick事件调用TestCallBack

  TestCallBack(CBFunc)//函数的参数CBFunc为回调函数的地址

(本文来源于图老师网站,更多请访问https://m.tulaoshi.com/bianchengyuyan/)

  函数调用创建线程后立刻返回,应用程序可以同时干别的事情去了。现在可以看到屏幕上不停的显示字符串,表示dll里创建的线程运行正常。一会之后,线程延时部分结束结束,vc的应用程序弹出MessageBox,表示回调函数被调用并显示根据Param1,Param2运算的结果,Delphi的程序edit控件里的文本则被改写成Param1,Param2的运算结果。

  可见使用回调函数的编程模式,可以根据不同的需求传递不同的回调函数地址,或者定义各种回调函数的原形(同时也需要改变使用回调函数的参数和返回值约定),实现多种回调事件处理,可以使程序的控制灵活多变,也是一种高效率的,清晰的程序模块之间的耦合方式。在一些异步或复杂的程序系统里尤其有用--你可以在一个模块(如DLL)里专心实现模块核心的业务流程和技术功能,外围的扩展的功能只给出一个回调函数的接口,通过调用其他模块传递过来的回调函数地址的方式,将后续处理无缝地交给另一个模块,随它按自定义的方式处理。

  本文的例子使用了在DLL里的多线程延时后调用回调函数的方式,只是为了突出一下回调函数的效果,其实只要是在本进程之内,都可以随你高兴可以把函数地址传递来传递去,当成回调函数使用。

  这样的编程模式原理非常简单单一:就是把函数也看成一个指针一个地址来调用,没有什么别的复杂的东西,仅仅是编程里的一个小技巧。至于回调函数模式究竟能为你带来多少好处,就看你是否使用,如何使用这种编程模式了。

展开更多 50%)
分享

猜你喜欢

使用回调函数(VC & Delphi)

编程语言 网络编程
使用回调函数(VC & Delphi)

深入VC回调函数的使用详解

编程语言 网络编程
深入VC回调函数的使用详解

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

回调函数与Delphi的事件模型

Delphi
回调函数与Delphi的事件模型

怎样在vc、delphi中使用mysql

编程语言 网络编程
怎样在vc、delphi中使用mysql

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

怎样在vc、delphi中使用mysql(mysql odbc驱动的使用)

MySQL mysql数据库
怎样在vc、delphi中使用mysql(mysql odbc驱动的使用)

关于Javascript中split函数用回车换行问题

Web开发
关于Javascript中split函数用回车换行问题

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

TMouse

TMouse

XML简明教程(1)什么是XML元素

XML简明教程(1)什么是XML元素
下拉加载更多内容 ↓