VS下 dllimport與dllexport作用與區別
原文:http://www.cnblogs.com/foohack/p/4119207.html
我相信寫WIN32程式的人,做過DLL,都會很清楚__declspec(dllexport)的作用,它就是為了省掉在DEF檔案中手工定義匯出哪些函式的一個方法。當然,如果你的DLL裡全是C++的類的話,你無法在DEF裡指定匯出的函式,只能用__declspec(dllexport)匯出類。但是,MSDN文件裡面,對於__declspec(dllimport)的說明讓人感覺有點奇怪,先來看看MSDN裡面是怎麼說的:
不使用 __declspec(dllimport) 也能正確編譯程式碼,但使用 __declspec(dllimport) 使編譯器可以生成更好的程式碼。編譯器之所以能夠生成更好的程式碼,是因為它可以確定函式是否存在於 DLL 中,這使得編譯器可以生成跳過間接定址級別的程式碼,而這些程式碼通常會出現在跨 DLL 邊界的函式呼叫中。但是,必須使用 __declspec(dllimport) 才能匯入 DLL 中使用的變數。
初看起來,這段話前面的意思是,不用它也可以正常使用DLL的匯出庫,但最後一句話又說,必須使用 __declspec(dllimport) 才能匯入 DLL 中使用的變數這個是什麼意思??
那我就來試驗一下,假定,你在DLL裡只匯出一個簡單的類,注意,我假定你已經在專案屬性中定義了 SIMPLEDLL_EXPORT
SimpleDLLClass.h
#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT
#endif
class DLL_EXPORT SimpleDLLClass
{
public:
SimpleDLLClass();
virtual ~SimpleDLLClass();
virtual getValue() { return m_nValue;};
private:
int m_nValue;
};
SimpleDLLClass.cpp
#include "SimpleDLLClass.h"
SimpleDLLClass::SimpleDLLClass()
{
m_nValue=0;
}
SimpleDLLClass::~SimpleDLLClass()
{
}
然後你再使用這個DLL類,在你的APP中include SimpleDLLClass.h時,你的APP的專案不用定義 SIMPLEDLL_EXPORT 所以,DLL_EXPORT 就不會存在了,這個時候,你在APP中,不會遇到問題。這正好對應MSDN上說的__declspec(dllimport)定義與否都可以正常使用。但我們也沒有遇到變數不能正常使用呀。那好,我們改一下SimpleDLLClass,把它的m_nValue改成static,然後在cpp檔案中加一行
int SimpleDLLClass::m_nValue=0;
如果你不知道為什麼要加這一行,那就回去看看C++的基礎。 改完之後,再去LINK一下,你的APP,看結果如何,結果是LINK告訴你找不到這個m_nValue。明明已經定義了,為什麼又沒有了??肯定是因為我把m_nValue定義為static的原因。但如果我一定要使用Singleton的Design Pattern的話,那這個類肯定是要有一個靜態成員,每次LINK都沒有,那不是完了? 如果你有Platform SDK,用裡面的Depend程式看一下,DLL中又的確是有這個m_nValue匯出的呀。
再回去看看我引用MSDN的那段話的最後一句。 那我們再改一下SimpleDLLClass.h,把那段改成下面的樣子:
#ifdef SIMPLEDLL_EXPORT
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
再LINK,一切正常。原來dllimport是為了更好的處理類中的靜態成員變數的,如果沒有靜態成員變數,那麼這個__declspec(dllimport)無所謂。
_declspec(dllexport)與_declspec(dllimport)
都是DLL內的關鍵字,即匯出與匯入。他們是將DLL內部的類與函式以及資料匯出與匯入時使用的。主要區別在於,dllexport是在這些類、函式以 及資料的申明的時候使用。用過表明這些東西可以被外部函式使用,即(dllexport)是把DLL中的相關程式碼(類,函式,資料)暴露出來為其他應用程 序使用。使用了(dllexport)關鍵字,相當於聲明瞭緊接在(dllexport)關鍵字後面的相關內容是可以為其他程式使用的。而 dllimport關鍵字是在外部程式需要使用DLL內相關內容時使用的關鍵字。當一個外部程式要使用DLL內部程式碼(類,函式,全域性變數)時,只需要在 程式內部使用(dllimport)關鍵字宣告需要使用的程式碼就可以了,即(dllimport)關鍵字是在外部程式需要使用DLL內部相關內容的時候才 使用。(dllimport)作用是把DLL中的相關程式碼插入到應用程式中。
_declspec(dllexport)與_declspec(dllimport)是相互呼應,只有在DLL內部用dllexport作了宣告,才能 在外部函式中用dllimport匯入相關程式碼。實際上,在應用程式訪問DLL時,實際上就是應用程式中的匯入函式與DLL檔案中的匯出函式進行連結。而 且連結的方式有兩種:隱式迎接和顯式連結。
隱式連結是指通過編譯器提供給應用程式關於DLL的名稱和DLL函式的連結地址,面在應用程式中不需要顯式地將DLL載入到記憶體,即在應用程式中使用dllimport即表明使用隱式連結。不過不是所有的隱式連結都使用dllimport。
顯式連結剛同應用程式用語句顯式地載入DLL,編譯器不需要知道任何關DLL的資訊
以下是一個DLL標頭檔案的正規編寫方式:
1 #ifdef DIALOG_MAINMENU_EXPORTS 2 #define DIALOG_MAINMENU_API __declspec(dllexport) 3 #else 4 #define DIALOG_MAINMENU_API __declspec(dllimport) 5 #endif 6 7 class Dialog_MainMenu { 8 public: 9 static DIALOG_MAINMENU_API enum GAME_STATES { 10 MAINMENU, GAME, OPTIONS, CREDITS, QUIT 11 }; 12 static DIALOG_MAINMENU_API GAME_STATES CurrentGameState; 13 DIALOG_MAINMENU_API GAME_STATES GetState(); 14 };
以下是解釋:
OK - when you compile the dll - you are exporting the types. So, you need to define
the static member in .cpp
file of the dll. You also need to make sure that you have
enabled the definition of DIALOG_MAINMENU_EXPORTS
in compiler settings. This will make sure types are exported.
Now, when you link the console application with the dll - you will #include
dll's header and dont enable any definition of DIALOG_MAINMENU_EXPORTS
in compiler settings
(just leave the settings default). This will make the compiler understand that now you are importing the types from your dll into exe application.
references:
http://stackoverflow.com/questions/2481138/unresolved-external-symbol
http://stackoverflow.com/questions/17901973/unresolved-external-symbol-declspecdllimport