CALLBACK回調函數使用之一

凡是由你設計卻由windows系統呼叫的函數,統稱爲callback函數。某些API函數要求以callback作爲你參數之一。如SetTimer,LineDDA,EnumObjects。

回調函數是由開發者按照一定的原形進行定義的函數(每個回調函數都必須遵循這個原則來設計)

例如:
----------------------------------------
BOOL CALLBACK DialogProc(
    
     HWND hwndDlg, // handle of dialog box
     UINT uMsg, // message
     WPARAM wParam, // first message parameter
     LPARAM lParam // second message parameter
     );
----------------------------------------
說明:
回調函數必須有關鍵詞 CALLBACK;
回調函數本身必須是全局函數或者靜態函數,不可定義爲某個特定的類的成員函數

2 回調函數並不由開發者直接調用執行(只是使用系統接口API函數作爲起點)
3 回調函數通常作爲參數傳遞給系統API,由該API來調用
4 回調函數可能被系統API調用一次,也可能被循環調用多次

 

示範EnumObjects,發現某個Device Context的GDI obect 符合我們的形態時,呼叫callback函數.

假設我們有一個CMycalss如下:

class CMyclass {
private :
  int nCount;
  int CALLBACK _export
  EnumObjectsProc(LPSTR lpLogObject, LPSTR lpData);
public :
  void enumIt(CDC& dc);
}

void CMyclass::enumIt(CDC& dc)
{
  // 註冊 callback 函式
  dc.EnumObjects(OBJ_BRUSH, EnumObjectsProc, NULL);
}

C++編譯器針對CMyclass::enumIt實際作出的碼相當於:

void CMyclass::enumIt(CDC& dc)
{
  // 註冊 callback 函式
  CDC::EnumObjects(OBJ_BRUSH, EnumObjectsProc, NULL, (CDC *)&dc);
}

你所看到的最後一個參數其實是this指針,類的成員函數靠着this指針才得以抓到正確對象資料. 而nCount = 0;其實是this->nCount = 0; 基於相同的道理,上例中的EnumObjectProc既然是一個成員函數,C++編譯器也會爲它多準備一個隱藏參數.問題出現,  callback函數給windows呼叫用的,windows並不經由任何對象呼叫這個函數,也就無需傳遞this指針給callback函數,也是導致堆棧中有一個隨機參數會成爲this指針,而其結果當然是程序的崩潰了.

因此要把某個函數作爲callback函數,就必須告訴C++編譯器,不要放this指針作爲該函數的最後一個參數,兩個方法可以做到這一點,

1 .不要使用類的成員函數(也就是說 要使用全局函數) 作爲callback函數.

2. 使用static成員函數,也就是在函數前加上static修飾詞.

第一種做法相當於在C語言中使用callback函數,第二種做法接近OO精神.進一步而言,C++中的static函數特性是,即使對象還沒有產生,static成員已經存在(函數或參數都如此).換句話說,物件產生之前你已經可以呼叫類的static函數或者使用類的static變量了;也就是說凡是宣告爲static的東西,(不管函數或變量)都並不和對象結合在一起,它們是類的一部分,不屬於對象

Q 劉虎翼:
    編程工具: C++ BUILDER 3.0
    操作系統: WIN98
    我想在C++ 中使用回調函數,請問它的內在機制如何,另外怎麼定義。我用DialogBox函數時,如何使用回調函數? 它和鉤子函數有何不同?多謝指教!!!拜託!!!

A回答:

    使用回調函數實際上就是在調用某個函數(通常是API函數)時,將自己的一個函數(這個函數爲回調函數)的地址作爲參數傳遞給那個函數。而那個函數在需要的時候,利用傳遞的地址調用回調函數,這時你可以利用這個機會在回調函數中處理消息或完成一定的操作。至於如何定義回調函數,跟具體使用的API函數有關,一般在幫助中有說明回調函數的參數和返回值等。C++中一般要求在回調函數前加CALLBACK,這主要是說明該函數的調用方式。DialogBox的回調函數實際上是個窗口過程,用來處理所有消息。其定義爲:
    BOOL CALLBACK DialogProc(
    
     HWND hwndDlg, // handle of dialog box
     UINT uMsg, // message
     WPARAM wParam, // first message parameter
     LPARAM lParam // second message parameter
     );
    在Win32 API中有詳細說明。一般使用C++ Builder或MFC的往往沒有使用SDK編程的經驗,建議找一些SDK編程的書看一下,否則很難理解如何使用窗口過程。
    至於鉤子函數,只是回調函數的一個特例。習慣上把與SetWindowsHookEx函數一起使用的回調函數稱爲鉤子函數。也有人把利用VirtualQueryEx安裝的函數稱爲鉤子函數,不過這種叫法不太流行。
    
    frank的意見:
    我對回調函數的理解雖然粗淺,但是我覺得會讓人更容易理解:回調函數就相當於一箇中斷處理函數,由系統在符合你設定的條件時自動調用。爲此,你需要做三件事:1,聲明;2,定義;3,設置觸發條件,就是在你的函數中把你的回調函數名稱轉化爲地址作爲一個參數,以便於系統調用。
    聲明和定義時應注意:回調函數由系統調用,所以可以認爲它屬於WINDOWS系統。不要把它當作你的某個類的成員函數。
    
    ping的意見:
    frank說:回調函數屬於WINDOWS系統。我覺得不應該說回調函數是屬於系統的。應該說是程序把這段代碼的觸發交由系統來做。而這種做法是WINDOWS提供的處理機制吧,因爲消息是系統一手掌握着的,由系統來調用我們的程序對消息的處理部分,這樣子會比較方便。不然我們又得花力氣去讀消息列表了。(不知道我說的對不對,接觸系統還不深,請高手指教哦)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章