由__declspec宏引發的關於輸出函數方法的問題

首先,__declspec其實就是一個函數調用規範,關於函數調用規範,從網上看了下各位高人的介紹,常用的有4種:__cdecl,__stdcall,__fastcall和__declspec。

    __cdecl,__stdcall,__fastcall規定了參數出入棧的順序和方法,如果只用VC編程的話可以不用關心,但是要在C++和Pascal等其他語言通信的時候就要注意了,只有用相同的方法才能夠調用成功。

    __declspec主要是用於說明DLL的引出函數的,在某些情況下用__declspec(dllexport)在DLL中生命引出函數,比用傳統的DEF文件方便一些。在普通程序中也可以用__declspec(dllimport)說明函數是位於另一個DLL中的導出函數。

    說白了其實就是在函數聲明的時候多加一個關鍵字。比如最常見的很多API函數就是象這樣聲明的:  
  int   WINAPI   MessageBoxA(HWND,LPCSTR,LPSTR,UINT);  
    而WINAPI實際上就是__stdcall:)

    大多數API都採用__stdcall調用規範,這是因爲幾乎所有的語言都支持__stdcall調用。相比之下,__cdecl只有在C語言中才能用。但是__cdecl調用有一個特點,就是能夠實現可變參數的函數調用,比如printf,這用__stdcall調用是不可能的。
  
    __fastcall這種調用規範比較少見,但是在Borland   C++   Builder中比較多的採用了這種調用方式。
  
    如果有共享代碼的需要,比如寫DLL,推薦的方法是用__stdcall調用,因爲這樣適用範圍最廣。如果是C++語言寫的代碼供Delphi這樣的語言調用就必須聲明爲__stdcall,因爲Pascal不支持cdecl調用(或許Delphi的最新版本能夠支持也說不定,這個我不太清楚)。在其他一些地方,比如寫COM組件,幾乎都用的是stdcall調用。在VC或Delphi或C++Builder裏面都可以從項目設置中更改默認的函數調用規範,當然你也可以在函數聲明的時候加入__stdcall,__cdecl,__fastcall關鍵字來明確的指示本函數用哪種調用規範。

    __declspec一般都是用來聲明DLL中的導出函數。這個關鍵字也有一些其他的用法,不過非常罕見。

    關於DLL的函數:  
    動態鏈接庫中定義有兩種函數:導出函數(export   function)和內部函數(internal   function)。  
    導出函數可以被其它模塊調用,內部函數在定義它們的DLL程序內部使用。

    輸出函數的方法有以下幾種:  
    
1、傳統的方法  
    
    在模塊定義文件的EXPORT部分指定要輸入的函數或者變量。語法格式如下:  
    entryname[=internalname]   [@ordinal[NONAME]]   [DATA]   [PRIVATE]  
    
    其中:  
    
    entryname是輸出的函數或者數據被引用的名稱;  
    
    internalname同entryname;  
    
    @ordinal表示在輸出表中的順序號(index);  
    
    NONAME僅僅在按順序號輸出時被使用(不使用entryname);  
    
    DATA表示輸出的是數據項,使用DLL輸出數據的程序必須聲明該數據項爲_declspec(dllimport)。  
    
    上述各項中,只有entryname項是必須的,其他可以省略。  
    
    對於“C”函數來說,entryname可以等同於函數名;但是對“C++”函數(成員函數、非成員函數)來說,entryname是修飾名。可以從.map映像文件中得到要輸出函數的修飾名,或者使用DUMPBIN   /SYMBOLS得到,然後把它們寫在.def文件的輸出模塊。DUMPBIN是VC提供的一個工具。  
    
    如果要輸出一個“C++”類,則把要輸出的數據和成員的修飾名都寫入.def模塊定義文件。  
    
2、在命令行輸出  
    
    對鏈接程序LINK指定/EXPORT命令行參數,輸出有關函數。  
    
3、使用MFC提供的修飾符號_declspec(dllexport)  
    
    在要輸出的函數、類、數據的聲明前加上_declspec(dllexport)的修飾符,表示輸出。__declspec(dllexport)在C調用約定、C編譯情況下可以去掉輸出函數名的下劃線前綴。extern   "C"使得在C++中使用C編譯方式成爲可能。在“C++”下定義“C”函數,需要加extern   “C”關鍵詞。用extern   "C"來指明該函數使用C編譯方式。輸出的“C”函數可以從“C”代碼裏調用。  
    
    例如,在一個C++文件中,有如下函數:  
    extern   "C"   {void   __declspec(dllexport)   __cdecl   Test(int   var);}  
    其輸出函數名爲:Test    
    
    MFC提供了一些宏,就有這樣的作用。  
    
    AFX_CLASS_IMPORT:__declspec(dllexport)  
    
    AFX_API_IMPORT:__declspec(dllexport)  
    
    AFX_DATA_IMPORT:__declspec(dllexport)  
    
    AFX_CLASS_EXPORT:__declspec(dllexport)  
    
    AFX_API_EXPORT:__declspec(dllexport)  
    
    AFX_DATA_EXPORT:__declspec(dllexport)  
    
    AFX_EXT_CLASS:   #ifdef   _AFXEXT    
    AFX_CLASS_EXPORT  
    #else  
    AFX_CLASS_IMPORT  
    
    AFX_EXT_API:#ifdef   _AFXEXT  
    AFX_API_EXPORT  
    #else  
    AFX_API_IMPORT  
    
    AFX_EXT_DATA:#ifdef   _AFXEXT  
    AFX_DATA_EXPORT  
    #else  
    AFX_DATA_IMPORT  
    
    像AFX_EXT_CLASS這樣的宏,如果用於DLL應用程序的實現中,則表示輸出(因爲_AFX_EXT被定義,通常是在編譯器的標識參數中指定該選項/D_AFX_EXT);如果用於使用DLL的應用程序中,則表示輸入(_AFX_EXT沒有定義)。  
    
    要輸出整個的類,對類使用_declspec(_dllexpot);要輸出類的成員函數,則對該函數使用_declspec(_dllexport)。如:  
    
  class   AFX_EXT_CLASS   CTextDoc   :   public   CDocument  
  {  
  …  
  }  
    
  extern   "C"   AFX_EXT_API   void   WINAPI   InitMYDLL();  
    
  這幾種方法中,最好採用第三種,方便好用;其次是第一種,如果按順序號輸出,調用效率會高些;  
  最次是第二種。

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