天天看點

記錄C++庫封裝/OCX封裝遇到的壓棧順序導緻的問題。

記錄C++庫封裝/OCX封裝遇到的壓棧順序導緻的問題。

    事情是這樣,項目中使用VC++封裝了API動态庫xxx.dll,此庫中的所有API接口都顯示聲明了__stdcall,也就是說API函數壓棧順序是從右至左,棧空間的釋放由函數自己負責。     動态庫的使用方是MFC程式,而__cdecl是VC++預設的調用規則,在一個回調函數中沒有顯示寫明調用規則,其實就用了預設的__cdecl,然後災難就這麼發生了,隻要涉及到這個回調函數,調試程式就彈出一個不明是以的視窗如下:

記錄C++庫封裝/OCX封裝遇到的壓棧順序導緻的問題。

其實很早之前遇到并解決過此問題,仔細對比以前的項目代碼後找出這個差別,同一個坑跌了倆次,紀念一下!

另外__stdcall和__cdecl兩種調用的差別,很多同學可能還不清楚,特轉發一下: 原貼:http://blog.csdn.net/dengziliang001/article/details/17448789

(1) _stdcall調用

  _stdcall是Pascal程式的預設調用方式,參數采用從右到左的壓棧方式,被調函數自身在傳回前清空堆棧。

  WIN32 Api都采用_stdcall調用方式,這樣的宏定義說明了問題:

  #define WINAPI _stdcall

  按C編譯方式,_stdcall調用約定在輸出函數名前面加下劃線,後面加“@”符号和參數的位元組數,形如。

(2) _cdecl調用

  _cdecl是C/C++的預設調用方式,參數采用從右到左的壓棧方式,傳送參數的記憶體棧由調用者維護。_cedcl約定的函數隻能被C/C++調用,每一個調用它的函數都包含清空堆棧的代碼,是以産生的可執行檔案大小會比調用_stdcall函數的大。

  由于_cdecl調用方式的參數記憶體棧由調用者維護,是以變長參數的函數能(也隻能)使用這種調用約定。關于C/C++中變長參數(…)的問題,筆者将另文詳述。

  由于Visual C++預設采用_cdecl 調用方式,是以VC中中調用DLL時,使用者應使用_stdcall調用約定。

  按C編譯方式,_cdecl調用約定僅在輸出函數名前面加下劃線,形如_functionname。

  (3) _fastcall調用

  _fastcall調用較快,它通過CPU内部寄存器傳遞參數。

  按C編譯方式,_fastcall調用約定在輸出函數名前面加“@”符号,後面加“@”符号和參數的位元組數,形如@[email protected]。

幾乎我們寫的每一個WINDOWS API函數都是__stdcall類型的,為什麼??

     首先,我們談一下兩者之間的差別:

       WINDOWS的函數調用時需要用到棧(STACK,一種先入後出的存儲結構)。當函數

調用完成後,棧需要清除,這裡就是問題的關鍵,如何清除??

       如果我們的函數使用了_cdecl,那麼棧的清除工作是由調用者,用COM的術語來講

就是客戶來完成的。這樣帶來了一個棘手的問題,不同的編譯器産生棧的方式不盡相同

,那麼調用者能否正常的完成清除工作呢?答案是不能。

       如果使用__stdcall,上面的問題就解決了,函數自己解決清除工作。是以,在跨

(開發)平台的調用中,我們都使用__stdcall(雖然有時是以WINAPI的樣子出現)。

       那麼為什麼還需要_cdecl呢?當我們遇到這樣的函數如fprintf()它的參數是可變

的,不定長的,被調用者事先無法知道參數的長度,事後的清除工作也無法正常的進行

,是以,這種情況我們隻能使用_cdecl。

       到這裡我們有一個結論,如果你的程式中沒有涉及可變參數,最好使用__stdcal