程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 使用回調函數(VC & Delphi)

使用回調函數(VC & Delphi)

編輯:關於VC++

回調函數是一個很有用,也很重要的概念。當發生某種事件時,系統或其他函數將會自動調用你定義的一段函數。回調函數在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;

實際上是聲明了一個返回值為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(;Step<200;Step++)
   {
    StartTick=GetTickCount();
    /*這一段為線程交出部分運行時間以讓系統處理其他事務*/
    for(;GetTickCount()-StartTick<10;)
    {
     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為回調函數的地址

函數調用創建線程後立刻返回,應用程序可以同時干別的事情去了。現在可以看到屏幕上不停的顯示字符串,表示dll裡創建的線程運行正常。一會之後,線程延時部分結束結束,vc的應用程序彈出MessageBox,表示回調函數被調用並顯示根據Param1,Param2運算的結果,Delphi的程序edit控件裡的文本則被改寫成Param1,Param2的運算結果。

可見使用回調函數的編程模式,可以根據不同的需求傳遞不同的回調函數地址,或者定義各種回調函數的原形(同時也需要改變使用回調函數的參數和返回值約定),實現多種回調事件處理,可以使程序的控制靈活多變,也是一種高效率的,清晰的程序模塊之間的耦合方式。在一些異步或復雜的程序系統裡尤其有用--你可以在一個模塊(如DLL)裡專心實現模塊核心的業務流程和技術功能,外圍的擴展的功能只給出一個回調函數的接口,通過調用其他模塊傳遞過來的回調函數地址的方式,將後續處理無縫地交給另一個模塊,隨它按自定義的方式處理。

本文的例子使用了在DLL裡的多線程延時後調用回調函數的方式,只是為了突出一下回調函數的效果,其實只要是在本進程之內,都可以隨你高興可以把函數地址傳遞來傳遞去,當成回調函數使用。

這樣的編程模式原理非常簡單單一:就是把函數也看成一個指針一個地址來調用,沒有什麼別的復雜的東西,僅僅是編程裡的一個小技巧。至於回調函數模式究竟能為你帶來多少好處,就看你是否使用,如何使用這種編程模式了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved