天天看點

__stdcall ....

1.如果函數func是__cdecl(預設調用方式),調用時情況如下  

__stdcall ....

int    main()  

__stdcall ....
__stdcall ....

   ... {  

__stdcall ....

  //參數從右到左壓棧  

__stdcall ....

  push   4  

__stdcall ....

  push   3  

__stdcall ....

  push   2  

__stdcall ....

  push   1  

__stdcall ....

  call   func  

__stdcall ....

  add   esp   0x10   //調用者恢複堆棧指針esp,4個參數的大小是0x10(4x4)  

__stdcall ....

  }    

  2.如果函數func是__stdcall,調用時情況如下  

__stdcall ....

int    main()  

__stdcall ....
__stdcall ....

   ... {  

__stdcall ....

  //參數從右到左壓棧  

__stdcall ....

  push   4  

__stdcall ....

  push   3  

__stdcall ....

  push   2  

__stdcall ....

  push   1  

__stdcall ....

  call   func  

__stdcall ....

                //恢複堆棧指針由被調用者func負責,方法是"ret   0x10"  

__stdcall ....

  }    

  3.如果函數func是__pascal,調用情況如下  

__stdcall ....

int    main()  

__stdcall ....
__stdcall ....

   ... {  

__stdcall ....

  //參數從左到右壓棧  

__stdcall ....

  push   1  

__stdcall ....

  push   2  

__stdcall ....

  push   3  

__stdcall ....

  push   4  

__stdcall ....

  call   func  

__stdcall ....

  //恢複堆棧指針由被調用者func負責,方法是"ret   0x10"  

__stdcall ....

  }    

  3.如果函數func是__fastcall,調用情況如下  

__stdcall ....

int    main()  

__stdcall ....
__stdcall ....

   ... {  

__stdcall ....

  //參數先用ecx,   edx,   eax傳遞,然後再壓棧  

__stdcall ....

  //不進棧  

__stdcall ....

  //(不知為什麼,幫助中寫的是從左到右傳遞的,  

__stdcall ....

  //是不是錯了,還是bcb6和bcb5的不一樣)  

__stdcall ....

  push   4  

__stdcall ....

  mov   ecx 3  

__stdcall ....

  mov   edx 2  

__stdcall ....

  mov   eax 1  

__stdcall ....

  call   func  

__stdcall ....

  //恢複堆棧指針由被調用者func負責,方法是"ret   0x04",  

__stdcall ....

  //因為隻進棧一個參數,其餘用寄存器傳遞,是以用ret   0x04恢複  

__stdcall ....

  }    

    通過棧傳遞,被調用的函數在傳回前清理傳送參數的記憶體棧,但不同的是函數名的修飾部分。   

    1、stdcall是pascal程式的預設調用方式,通常用于win32   api中,函數采用從右到左的壓棧方式,自己在退出時清空堆棧。vc将函數編譯後會在函數名前面加上下劃線字首,在函數名後加上"@"和參數的位元組數。   

    2、c調用約定按從右至左的順序壓參數入棧,由調用者把參數彈出棧。對于傳送參數的記憶體棧是由調用者來維護的。另外,在函數名修飾約定方面也有所不同。   

    _cdecl是c和c++程式的預設調用方式。每一個調用它的函數都包含清空堆棧的代碼,是以産生的可執行檔案大小會比調用_stdcall函數的大。函 數采用從右到左的壓棧方式。vc将函數編譯後會在函數名前面加上下劃線字首。是mfc預設調用約定。   

    3、__fastcall調用約定是“人”如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數的或更小的參數,剩下的參數仍舊自右向左壓棧傳送, 被調用的函數在傳回前清理傳送參數的記憶體棧),在函數名修飾約定方面,它和前兩者均不同。   

    _fastcall方式的函數采用寄存器傳遞參數,vc将函數編譯後會在函數名前面加上"@"字首,在函數名後加上"@"和參數的位元組數。           

    4、thiscall僅僅應用于“c++”成員函數。this指針存放于cx寄存器,參數從右到左壓。thiscall不是關鍵詞,是以不能被程式員指定。   

    5、naked   call采用1-4的調用約定時,如果必要的話,進入函數時編譯器會産生代碼來儲存esi,edi,ebx,ebp寄存器,退出函數時則産生代碼恢複這些 寄存器的内容。naked   call不産生這樣的代碼。naked  call不是類型修飾符,故必須和_declspec共同使用。   

     關鍵字   __stdcall、__cdecl和__fastcall可以直接加在要輸出的函數前,也可以在編譯環境的setting.../c/c++   /code   generation項選擇。當加在輸出函數前的關鍵字與編譯環境中的選擇不同時,直接加在輸出函數前的關鍵字有效。它們對應的指令行參數分别為/gz、 /gd和/gr。預設狀态為/gd,即__cdecl。   

    要完全模仿pascal調用約定首先必須使用__stdcall調用約定,至于函數名修飾約定,可以通過其它方法模仿。還有一個值得一提的是winapi 宏,windows.h支援該宏,它可以将出函數翻譯成适當的調用約定,在win32中,它被定義為__stdcall。使用winapi宏可以建立自己 的apis。  

  2)名字修飾約定  

  1、修飾名(decoration   name)  

  “c”或者“c++”函數在内部通過修飾名識别。修飾名是編譯器在編譯函數定義或者原型時生成的字元串。有些情況下使用函數的修飾名是必要的,如在子產品定 義檔案裡頭指定輸出“c++”重載函數、構造函數、析構函數,又如在彙編代碼裡調用“c””或“c++”函數等。  

  修飾名由函數名、類名、調用約定、傳回類型、參數等共同決定。  

  2、名字修飾約定随調用約定和編譯種類(c或c++)的不同而變化。函數名修飾約定随編譯種類和調用約定的不同而不同,下面分别說明。   

    a、c編譯時函數名修飾約定規則:  

    __stdcall調用約定在輸出函數名前加上一個下劃線字首,後面加上一個“@”符号和其參數的位元組數,格式為[email protected]。  

    __cdecl調用約定僅在輸出函數名前加上一個下劃線字首,格式為_functionname。  

    __fastcall調用約定在輸出函數名前加上一個“@”符号,後面也是一個“@”符号和其參數的位元組數,格式為@[email protected]。   

    它們均不改變輸出函數名中的字元大小寫,這和pascal調用約定不同,pascal約定輸出的函數名無任何修飾且全部大寫。   

    b、c++編譯時函數名修飾約定規則:   

    __stdcall調用約定:   

    1、以“?”辨別函數名的開始,後跟函數名;   

    2、函數名後面以 “@@yg”辨別參數表的開始,後跟參數表;   

    3、參數表以代号表示:  

                            x--void   ,  

                            d--char,  

                            e--unsigned   char,  

                            f--short,  

                            h--int,  

                            i--unsigned   int,  

                            j--long,  

                            k--unsigned   long,  

                            m--float,  

                            n--double,  

                            _n--bool,  

                            ....  

                            pa--表示指針,後面的代号表明指針類型,如果相同類型的指針連續出現,以“0”代替,一個“0”代表一次重複;   

    4、參數表的第一項為該函數的傳回值類型,其後依次為參數的資料類型,指針辨別在其所指資料類型前;     

    5、參數表後以 “@z”辨別整個名字的結束,如果該函數無參數,則以“z”辨別結束。   

    其格式為 “?functionname@@yg*****@z”或“?functionname@@yg*xz”,例如  

                      int   test1-----“?test1@@[email protected]”  

                      void   test2                                               -----“?test2@@ygxxz”   

    __cdecl調用約定:  

    規則同上面的_stdcall調用約定,隻是參數表的開始辨別由上面的 “@@yg”變為“@@ya”。  

    __fastcall調用約定:   

    規則同上面的_stdcall調用約定,隻是參數表的開始辨別由上面的 “@@yg”變為“@@yi”。   

    vc++對函數的省缺聲明是"__cedcl",将隻能被c/c++調用.  

    cb在輸出函數聲明時使用4種修飾符号  

  //__cdecl  

    cb的預設值,它會在輸出函數名前加_,并保留此函數名不變,參數按照從右到左的順序依次傳遞給棧,也可以寫成_cdecl和cdecl形式。  

  //__fastcall  

    她修飾的函數的參數将盡肯呢感地使用寄存器來處理,其函數名前加@,參數按照從左到右的順序壓棧;  

  //__pascal  

    它說明的函數名使用pascal格式的命名約定。這時函數名全部大寫。參數按照從左到右的順序壓棧;  

  //__stdcall  

    使用标準約定的函數名。函數名不會改變。使用__stdcall修飾時。參數按照由右到左的順序壓棧,也可以是_stdcall