天天看點

MFC中CALLBACK函數 和WINAPI函數的差別

凡是由你設計卻由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調用一次,也可能被循環調用多次

WINAPI見windef.h這個頭檔案

  #define WINAPI __stdcall  預設情況下,我們的函數調用都是遵循__stdcall這個規則的。當然,也有諸如__cdecl、__pascal等規則。  使用__stdcall還是__cdecl或__pascal,在純Windows程式設計下并非特别需要。  __stdcall:  1、進行函數調用,函數參數的入棧方式是最右邊先入棧。  2、同時__stdcall規定,子函數負責棧的回收(調用者隻負責壓棧). 題外話:__pascal的調用規則是從左到右,正好與__stdcall相反。  3、C調用約定(即用__cdecl關鍵字說明)(The C default calling convention)按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對于傳送參數的記憶體棧是由調用者來維護的(正因為如此,實作可變參數vararg的函數(如printf)隻能使用該調用約定)當然,這些工作是 應用程式自己完成的,不需要編寫者動手。彙編語言另當别論(POP SP POP BP等) 。另外,在函數名修飾約定方面也有所不同。 _cdecl是C和C++程式的預設調用方式。每一個調用它的函數都包含清空堆棧的代碼,是以産生的 可執行檔案大小會比調用_stdcall函數的大。函數采用從右到左的壓棧方式。VC将函數編譯後會在函數名前面加上下劃線字首。

MFC預設調用約定

  在函數調用過程中,會使用棧。__stdcall與__cdecl是兩種不同的函數調用約定,定義了函數參數入棧的順序,由調用函數還是被調用函數将參數彈出棧,以及産生函數修飾名的方法。關于這兩個調用約定的詳細資訊,讀者可參看MSDN。對于參數個數可變的函數,例如printf,使用的是__cdecl調用約定,Win32的API函數都遵循__stdcall調用約定。在VC++開發環境中,預設的編譯選項是__cdecl,對于那些需要__stdcall調用約定的函數,在聲明時必須顯式地加上__stdcall。在Windows程式中,回調函數必須遵循__stdcall調用約定,是以我們在聲明回調函數時要使用CALLBACK。使用CALLBACK而不是__stdcall的原因是為了告訴我們這是一個回調函數。注意,在Windows 98和Windows 2000下,聲明視窗過程函數時,即使不使用CALLBACK也不會出錯,但在Windows NT4.0下,則會出錯

WINAPI和CALLBACK這兩個宏有什麼差別呢?

_stdcall _cdecl _pascal _fastcall這些關鍵字是什麼意思,有什麼差別呢?

首先看MSDN裡給出的解釋,不過有些語焉不詳哦

WINAPI 

·Use in place of FAR PASCAL in API declarations. If you are writing a DLL with exported API entry points, you can use this for your own APIs. 

CALLBACK

·Use in place of FAR PASCAL in application callback routines such as window procedures and dialog procedures. 

再看看到底這兩個宏的内容是什麼吧

VC:WINDEF.h

#define CALLBACK    PASCAL //=_pascal,VC已經不支援直接使用_pascal了

#define WINAPI      CDECL //=_cdecl

BCB:windef.h

#define CALLBACK    __stdcall

#define WINAPI      __stdcall

引出了cdecl stdcall等一些可能很少見的關鍵字

那麼cdecl、pascal、stdcall、fastcall等修飾符号到底什麼意思呢?

非常簡單,就是關于堆棧的一些說明,首先是函數參數壓棧順序,其次是

壓入堆棧的内容由誰來清除,調用者還是函數自己?

這些開關用來告訴編譯器産生什麼樣的彙編代碼。

下面把差別清單如下:

Directive Parameter order  Clean-up Passes parameters in registers?

 register  Left-to-right   Routine   Yes

 pascal   Left-to-right   Routine   No

 cdecl   Right-to-left   Caller    No

 stdcall   Right-to-left   Routine   No

 safecall  Right-to-left   Routine   No

簡單說明:

__cdecl是C/C++和MFC程式預設使用的調用約定,也可以在函數聲明時加上__cdecl關鍵字來手工指定。采用__cdecl約定時,函數參數按照從右到左的順序入棧,并且由調用函數者把參數彈出棧以清理堆棧。是以,實作可變參數的函數隻能使用該調用約定。由于每一個使用__cdecl約定的函數都要包含清理堆棧的代碼,是以産生的可執行檔案大小會比較大。__cdecl可以寫成_cdecl。

        __stdcall調用約定用于調用Win32 API函數。采用__stdcal約定時,函數參數按照從右到左的順序入棧,被調用的函數在傳回前清理傳送參數的棧,函數參數個數固定。由于函數體本身知道傳進來的參數個數,是以被調用的函數可以在傳回前用一條ret n指令直接清理傳遞參數的堆棧。__stdcall可以寫成_stdcall。

        __fastcall約定用于對性能要求非常高的場合。__fastcall約定将函數的從左邊開始的兩個大小不大于4個位元組(DWORD)的參數分别放在ECX和EDX寄存器,其餘的參數仍舊自右向左壓棧傳送,被調用的函數在傳回前清理傳送參數的堆棧。__fastcall可以寫成_fastcall。 

·特别說明

1. 在預設情況下,采用__cdecl方式,是以可以省略.

2. WINAPI一般用于修飾動态連結庫中導出函數

3. CALLBACK僅用于修飾回調函數

4. 你可能已經發現,VC下和BCB下對WINAPI的定義不同,那麼你至少了解了

   為什麼不能直接從BCB下調用VC的dll的一個原因了。

MFC