DLL驅動源代碼中的WINAPI和APIENTRY理解

轉載出處:http://www.cnblogs.com/shenfengok/archive/2011/09/07/2169431.html


結論:WINAPI表示此函數是普通的winapi函數調用方式,APIENTRY則表明此函數是應用程序的入口點,相當於c代碼中的main()函數,WINAPI和APIENTRY其實都是__stdcall的宏定義。  


詳細分析:

msdn中對ThreadProc的定義有要求:DWORD WINAPI ThreadProc(LPVOID lpParameter);
不解爲什麼要用WINAPI宏定義,查了後發現下面的定義。於是乎需要區別__stdcall和__cdecl兩者的區別; 
#define CALLBACK       __stdcall
#define WINAPI             __stdcall
#define WINAPIV           __cdecl
#define APIENTRY        WINAPI
#define APIPRIVATE     __stdcall
#define PASCAL           __stdcall
#define cdecl                _cdecl
#ifndef CDECL
#define CDECL _cdecl
#endif


幾乎我們寫的每一個WINDOWS API函數都是__stdcall類型的,首先,需要了解兩者之間的區別: WINDOWS的函數調用時需要用到棧(STACK,一種先入後出的存儲結構)。當函數調用完成後,棧需要清楚,這裏就是問題的關鍵,如何清除??如果我 們的函數使用了_cdecl,那麼棧的清除工作是由調用者,用COM的術語來講就是客戶來完成的。這樣帶來了一個棘手的問題,不同的編譯器產生棧的方式不 盡相同,那麼調用者能否正常的完成清除工作呢?答案是不能。如果使用__stdcall,上面的問題就解決了,函數自己解決清除工作。所以,在跨(開發) 平臺的調用中,我們都使用__stdcall(雖然有時是以 WINAPI的樣子出現)。那麼爲什麼還需要_cdecl呢?當我們遇到這樣的函數如fprintf()它的參數是可變的,不定長的,被調用者事先無法知 道參數的長度,事後的清除工作也無法正常的進行,因此,這種情況我們只能使用_cdecl。到這裏我們有一個結論,如果你的程序中沒有涉及可變參數,最好 使用__stdcall關鍵字。

__cdecl,__stdcall是聲明的函數調用協議.主要是傳參和彈棧方面的不同.一般c++用的是__cdecl,windows裏大都用 的是__stdcall(API)
__cdecl 是C/C++和MFC程序默認使用的調用約定,也可以在函數聲明時加上__cdecl關鍵字來手工指定。採用__cdecl約定時,函數參數按照從右到左 的順序入棧,並且由調用函數者把參數彈出棧以清理堆棧。因此,實現可變參數的函數只能使用該調用約定。由於每一個使用__cdecl約定的函數都要包含清理堆棧的代碼,所以產生的可執行文件大小會比較大。__cdecl可以寫成_cdecl

__stdcall調用約定用於調用Win32 API函數。採用__stdcall約定時,函數參數按照從右到左的順序入棧,被調用的函數在返回前清理傳送參數的棧,函數參數個數固定。由於函數體本身 知道傳進來的參數個數,因此被調用的函數可以在返回前用一條ret n指令直接清理傳遞參數的堆棧。__stdcall可以寫成_stdcall。
__fastcall 約定用於對性能要求非常高的場合。__fastcall約定將函數的從左邊開始的兩個大小不大於4個字節(DWORD)的參數分別放在ECX和EDX寄存 器,其餘的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的堆棧。__fastcall可以寫成_fastcall

1、__stdcall函數調用修飾符
__stdcall:
_stdcall 調用約定相當於16位動態庫中經常使用的PASCAL調用約定。
在32位的VC++5.0中PASCAL調用約定不再被支持(實際上它已被定義爲 __stdcall。除了__pascal外,__fortran和__syscall也不被支持),取而代之的是__stdcall調用約定。兩者實質 上是一致的,即函數的參數自右向左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧,但不同的是函數名的修飾部分(關於函數名的修飾部分在後面將詳 細說明)。
_stdcall是Pascal程序的缺省調用方式,通常用於Win32 Api中,函數採用從右到左的壓棧方式,自己在退出時清空堆棧。VC將函數編譯後會在函數名前面加上下劃線前綴,在函數名後加上"@"和參數的字節數。

2、__cdecl函數調用修飾符
_cdecl:
_cdecl c調用約定, 按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對於傳送參數的內存棧是由調用者來維護的(正因爲如此,實現可變參數的函數只能使用該調用約定)。另 外,在函數名修飾約定方面也有所不同。
_cdecl是C和C++程序的缺省調用方式。每一個調用它的函數都包含清空堆棧的代碼,所以產生的可執行文件大小會比調用_stdcall函數的 大。函數採用從右到左的壓棧方式。VC將函數編譯後會在函數名前面加上下劃線前綴。是MFC缺省調用約定。

3、__fastcall函數調用修飾符
__fastcall:
__fastcall調用約定是"人"如其名,它的主要特點就是快,因爲它是通過寄存器來傳送參數的(實際上,它用ECX和EDX傳送前兩個雙字 (DWORD)或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的內存棧),在函數名修飾約定方面,它和前兩者均不同。
_fastcall方式的函數採用寄存器傳遞參數,VC將函數編譯後會在函數名前面加上"@"前綴,在函數名後加上"@"和參數的字節數。

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