輕鬆使用自己的回調函數

 
vcbear原創

文章級別: 小技巧

     回調函數是一個很有用,也很重要的概念。當發生某種事件時,系統或其他函數將會自動調用你定義的一段函數。回調函數在windows編程使用的場合很多,比如Hook回調函數:MouseProc,GetMsgProc以及EnumWindows,DrawState的回調函數等等,還有很多系統級的回調過程。本文不準備介紹這些函數和過程,而是談談實現自己的回調函數的一些經驗。

 

     之所以產生使用回調函數這個想法,是因爲現在使用VCDelphi混合編程,用VC寫的一個DLL程序進行一些時間比較長的異步工作,工作完成之後,需要通知使用DLL的應用程序:某些事件已經完成,請處理事件的後續部分。開始想過使用同步對象,文件影射,消息等實現DLL函數到應用程序的通知,後來突然想到可不可以在應用程序端先寫一個函數,等需要處理後續事宜的時候,在DLL裏直接調用這個函數即可。

  

      於是就動手,寫了個回調函數的原形。在VC Delphi裏都進行了測試

 

一:聲明回調函數類型。

       vc

              typedef int (WINAPI *PFCALLBACK)(int Param1,int Param2) ;

 

       Delph

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

 

       實際上是聲明瞭一個返回值爲int,傳入參數爲兩個int的指向函數的指針。

       由於C++PASCAL編譯器對參數入棧和函數返回的處理有可能不一致,把函數類型用WINAPI(WINAPI宏展開就是__stdcall)stdcall統一修飾。

 

二:聲明回調函數原形

 

       聲明函數原形

      vc

               int WINAPI CBFunc(int Param1,int Param2)

 

      Delphi

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

          

       

      以上函數爲全局函數,如果要使用一個類裏的函數作爲回調函數原形,把該類函數聲明爲靜態函數即可。

 

 

三: 回調函數調用調用者

 

         調用回調函數的函數我把它放到了DLL裏,這是一個很簡單的VC生成的WIN32 DLL.並使用DEF文件輸出其函數名 TestCallBack。實現如下:

 

              PFCALLBACK  gCallBack=0;

 

            void WINAPI TestCallBack(PFCALLBACK Func)

           {

                  if(Func==NULL)return;

                  gCallBack=Func;

                  DWORD ThreadID=0;

 

                  HANDLE hThread = CreateThread(

                                                                    NULL,

                                                                         NULL,

                                Thread1,

                             LPVOID(0),

                                                                         &ThreadID

                                                                          );

                   return;

             }

      此函數的工作把傳入的 PFCALLBACK Func參數保存起來等待使用,並且啓動一個線程。聲明瞭一個函數指針PFCALLBACK gCallBack保存傳入的函數地址。

 

 

四: 回調函數如何被使用:

           

         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);

                  return 0;

      }

 

  

五:萬事具備

        使用vcDelphi各建立了一個工程,編寫回調函數的實現部分

       VC

     int WINAPI CBFunc(int Param1,int Param2)

       {

               int res= Param1+Param2;

             TCHAR Buffer[256]="";

            sprintf(Buffer,"callback result = %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,表示回調函數被調用並顯示根據Param1Param2運算的結果,Delphi的程序edit控件裏的文本則被改寫成Param1Param2 的運算結果。

    

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

 

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

 

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

http://tb.blog.csdn.net/TrackBack.aspx?PostId=5983

發佈了8 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章