extern "C" __declspec(dllexport) __declspec(dll...

extern "C" __declspec(dllexport) __declspec(dllimport) 和 def

前面的extern "C"  __declspec(dllexport)  __declspec(dllimport)都是用於函數或者變量,甚至類的聲明的(能夠把extern "C"放在class的前面,可是編譯器會忽略掉,最後產生的仍是C++修飾符,而不是C修飾符)這樣的用法有個好處就是下面的代碼能夠在混有類的函數和變量上使用下面的宏,雖然對類不起做用:
#ifdef __cplusplus
extern "C"
{
//函數聲明
//變量聲明,變量通常前面都有extern
//類聲明,這個不起做用,編譯器直接忽略掉class前面的extern 「C」
#ifdef __cplusplus
}

#endif
C 和C++ 對應不一樣的調用約定,產生的修飾符也各不相同,以下:
調用約定 extern "C" 或 .c 文件 .cpp、.cxx 或 /TP

C 命名約定 (__cdecl)html

_test函數

?test@@ZAXXZ優化

Fastcall 命名約定 (__fastcall)spa

@test @0 .net

?test@@YIXXZ命令行

標準調用命名約定 (__stdcall)htm

_test@0 blog

?test@@YGXXZci


__declspec(dllexport)  __declspec(dllimport)通常也是使用宏的形式:
#ifdef ONEDLL_EXPORTS
#define ONEDLL_API __declspec(dllexport)
#else
#define ONEDLL_API __declspec(dllimport)
#endif
這樣在DLL代碼自己就是__declspec(dllexport) ,在使用DLL的程序中就變成了__declspec(dllimport),這兩標誌分別用來指明當前的函數將被導出,和當前函數是被導入的。
 

上面的兩個宏結合一下就是下面這樣的了:
//  下列 ifdef 塊是建立使從 DLL 導出更簡單的
//  宏的標準方法。此 DLL 中的全部文件都是用命令行上定義的 ONEDLL_EXPORTS
//  符號編譯的。在使用此 DLL 的
//  任何其餘項目上不該定義此符號。這樣,源文件中包含此文件的任何其餘項目都會將
//  ONEDLL_API 函數視爲是從 DLL 導入的,而此 DLL 則將用此宏定義的
//  符號視爲是被導出的。
#ifdef ONEDLL_EXPORTS
#define ONEDLL_API __declspec(dllexport)
#else
#define ONEDLL_API __declspec(dllimport)
#endif

//  此類是從 OneDll.dll 導出的
#ifdef __cplusplus
extern "C"
{
#endif
class ONEDLL_API COneDll {
public:
    COneDll(void);
    ~COneDll(void);
    
    // TODO: 在此添加您的方法。
    int m_a;
    int m_b;
    int *m_p;
    int m_n;

    void AddValue();

}
;

extern ONEDLL_API int nOneDll;

ONEDLL_API int fnOneDll(void);

#ifdef __cplusplus
}

#endif

若是調用模塊和被調用模塊都是C++(並且是同一種編成環境,如VC,甚至須要同一版本的VC),那麼就不須要extern 「C」了,由於這個標誌的做用就是用在函數和變量聲明前,不管是調用模塊,仍是被調用模塊,都將生成C修飾符,調用模塊將須要C修飾符的函數,而被調用模塊將產生C修飾符的函數,因此這個標誌在二者都是C++的時候使用並不受影響,不使用這個標誌,也不受影響。
可是若是C模塊要調用C++ 模塊,那麼C++模塊就須要使用extern 「C」,固然C不用,因爲是在頭文件的聲明中使用,因此使用下面的宏可以使得這個頭文件也在C中順利使用:
#ifdef __cplusplus
extern "C"
{
//函數聲明
//變量聲明,變量通常前面都有extern
//類聲明,這個不起做用,編譯器直接忽略掉class前面的extern 「C」
#ifdef __cplusplus
}

#endif

若是C++模塊要調用C模塊,那麼C++模塊仍是須要extern 「C」,固然C不用,因爲是在頭文件的聲明中使用,因此使用上面的宏一樣可以使得這個頭文件也在C中順利使用。

總結一下就是加上extern 「C」在什麼狀況下都沒錯,可是要注意函數重載的問題。



def文件是一種比較麻煩的方法,下面是MSDN中的部份內容:
 

模塊定義 (.def) 文件是包含一個或多個描述 DLL 各類屬性的 Module 語句的文本文件。若是不使用 __declspec(dllexport) 關鍵字導出 DLL 的函數,則 DLL 須要 .def 文件。get

.def 文件必須至少包含下列模塊定義語句:
1.文件中的第一個語句必須是 LIBRARY 語句。此語句將 .def 文件標識爲屬於 DLL。LIBRARY 語句的後面是 DLL 的名稱。連接器將此名稱放到 DLL 的導入庫中。
2.EXPORTS 語句列出名稱,可能的話還會列出 DLL 導出函數的序號值。經過在函數名的後面加上 @ 符和一個數字,給函數分配序號值。當指定序號值時,序號值的範圍必須是從 1 到 N,其中 N 是 DLL 導出函數的個數。

例如,包含實現二進制搜索樹的代碼的 DLL 看上去可能像下面這樣:

LIBRARY   BTREE
EXPORTS
   Insert    @1
   Delete   @2
   Member   @3
   Min   @4

提示:

若是但願優化 DLL 文件的大小,請對每一個導出函數使用 NONAME 屬性。使用 NONAME 屬性時,序號存儲在 DLL 的導出表中而非函數名中。若是導出許多函數,這樣作能夠節省至關多的空間。


其實 __declspec(dllexport)的做用就是讓編譯器按照某種預約的方式(前面大體解釋了這種方式的規則)來輸出導出函數及變量的符號,而def文件則是本身爲每個函數和變量指定導出符號,因此def是一個非自動化,手工很強的方式,不是特殊狀況的話,實在沒有必要浪費這些時間。
還有一個問題,就是使用def會把調用方式和 __declspec(dllexport)的做用所有覆蓋掉,因此還須要本身處理調用方式不一樣產生的錯誤。 通常使用def文件的狀況是你須要使用運行時加載,而且須要使用GetProcAddress函數得到函數地址,這個函數須要直接指明函數產生的導出符號,而能夠本身指定導出符號的方式就是使用def。 def文件的具體語法能夠看看msdn。
相關文章
相關標籤/搜索