在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
#endifclass 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匯出函數的兩種方式 DLL可以使用兩種方法將公用符號匯入到應用程式中或從 DLL 匯出函數: 產生 DLL 時使用模組定義 (.DEF) 檔案;在主應用程式的函數定義中使用 __declspec(dllimport) 或 __declspec(dllexport) 關鍵字。 使用 .DEF 檔案:模組定義 (.DEF) 檔案是包含一個或多個描述各種 DLL 屬性的 Module 語句的文字檔。如果不使用 __declspec(dllimport) 或 __declspec(dllexport) 匯出 DLL 函數,則 DLL 需要 .DEF 檔案。 使用 __declspec:32 位版的 Visual C++ 用 __declspec(dllimport) 和 __declspec(dllexport) 取代以前在 16 位版的 Visual C++ 中使用的 __export 關鍵字。_declspec(dllexport)的詳解 寫WIN32程式或做過DLL的人,都會很清楚__declspec(dllexport)的作用,它就是為了省掉在DEF檔案中手工定義匯出哪些函數的一個方法。當然,如果你的DLL裡全是C++的類的話,你無法在DEF裡指定匯出的函數,只能用__declspec(dllexport)匯出類。 MSDN文檔裡面對於__declspec(dllimport)的說明:
不使用__declspec(dllimport) 也能正確編譯代碼,但使用__declspec(dllimport)使編譯器可以產生更好的代碼。編譯器之所以能夠產生更好的代碼,是因為它可以確定函數是否存在於DLL 中,這使得編譯器可以產生跳過間接定址層級的代碼,而這些代碼通常會出現在跨DLL邊界的函數調用中。但是,必須使用__declspec(dllimport)才能匯入 DLL 中使用的變數。 初看起來,這段話前面的意思是,不用它也可以正常使用DLL的匯出庫,但最後一句話又說,必須使用__declspec(dllimport) 才能匯入 DLL 中使用的變數。 在Windows DLL編程時,可使用__declspec(dllimport)關鍵字匯入函數或者變數。函數的匯入 當你需要使用DLL中的函數時,往往不需要顯示地匯入函數,編譯器可自動完成。但如果你顯示地匯入函數,編譯器會產生品質更好的代碼。由於編譯器確切地知道了一個函數是否在一個DLL中,它就可以產生更好的代碼,不再需要間接的調用轉接。 Win32的PE格式(Portable Executable Format)把所有匯入地址放在一個匯入地址表中。下面用一個具體執行個體說明使用__declspec(dllimport)匯入函數和不使用的區別: 假設func是一個DLL中的函數,現在在要產生的.exe的main函數中調用func函數,並且不顯示地匯入func函數(即沒有:__declspec(dllimport)),程式碼範例如下: int main() { func(); }編譯器將產生類似這樣的調用代碼: call func然後,連結器把該調用翻譯為類似這樣的代碼: call 0x40000001 ; ox40000001是"func"的地址並且,連結器將產生一個Thunk,形如: 0x40000001: jmp DWORD PTR __imp_func這裡的imp_func是func函數在.exe的匯入地址表中的函數槽的地址。然後,載入器只需要在載入時更新.exe的匯入地址表即可。 而如果使用了__declspec(dllimport)顯示地匯入函數,那麼連結器就不會產生Thunk(如果不被要求的話),而直接產生一個間接調用。因此,下面的代碼: __declspec(dllimport) void func1(void);
void main(void)
{
func1();
}將調用如下調用指令: call DWORD PTR __imp_func1 因此,顯示地匯入函數能有效減少目標代碼(因為不產生Thunk)。另外,在DLL中使用DLL外的函數也可以這樣做,從而提高空間和時間效率。變數的匯入 與函數不同的是,在使用DLL中的變數時,需要顯示地匯入變數。使用__declspec(dllimport)關鍵字匯入變數。若在DLL中使用.def匯出變數,則應使用DATA修飾變數,而不是使用已經被遺棄的CONSTANT。因為CONSTANT可能需要使用指標間接訪問變數,不確定什麼時候會出問題。文章來源:http://chenuaizhang2008.blog.163.com/blog/static/7276357201011625214953/