VS下 dllimport與dllexport作用與區別


 

我相信寫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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM