欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

鏈接庫(kù)動(dòng)態(tài)鏈接庫(kù)詳細(xì)介紹

 更新時(shí)間:2012年11月16日 17:15:49   作者:  
靜態(tài)鏈接庫(kù).lib和動(dòng)態(tài)鏈接庫(kù).dll。其中動(dòng)態(tài)鏈接庫(kù)在被使用的時(shí)候,通常還提供一個(gè).lib,稱為引入庫(kù),它主要提供被Dll導(dǎo)出的函數(shù)和符號(hào)名稱,使得鏈接的時(shí)候能夠找到dll中對(duì)應(yīng)的函數(shù)映射
windows中,鏈接庫(kù)分為兩種類型:靜態(tài)鏈接庫(kù).lib和動(dòng)態(tài)鏈接庫(kù).dll。其中動(dòng)態(tài)鏈接庫(kù)在被使用的時(shí)候,通常還提供一個(gè).lib,稱為引入庫(kù),它主要提供被Dll導(dǎo)出的函數(shù)和符號(hào)名稱,使得鏈接的時(shí)候能夠找到dll中對(duì)應(yīng)的函數(shù)映射。
靜態(tài)鏈接庫(kù)和動(dòng)態(tài)鏈接庫(kù)的作用相似,都是提供給其他程序進(jìn)行調(diào)用的資源。其中,動(dòng)態(tài)鏈接庫(kù)的調(diào)用方法分隱式調(diào)用(靜態(tài)導(dǎo)入調(diào)用)和顯示調(diào)用(動(dòng)態(tài)導(dǎo)入調(diào)用)。 

編譯環(huán)境
Microsoft Visual Stdio 2010
--------------------------------------------------------------------------------
DLL導(dǎo)出符號(hào)
例,首先生成一個(gè)dll1.dll和dll1.lib
復(fù)制代碼 代碼如下:

// DLL1工程,dll1.cpp
// _declspec(dllexport)為導(dǎo)出符號(hào)
_declspec(dllexport) int add(int a, int b)
{
return a + b;
}

利用微軟的depends工具查看dll1.dll,導(dǎo)出的符號(hào)如下:
http://www.cnblogs.com/monotone/ 
其中各字段意義:Ordinal(符號(hào)序號(hào),后面使用GetProcAddress的時(shí)候,參考的數(shù)值),Hint(這個(gè)我也不是太明白,據(jù)說(shuō)是不用了解),F(xiàn)unction(這個(gè)就是函數(shù)導(dǎo)出后的符號(hào)名稱了),EntryPoint(這個(gè)是函數(shù)在DLL中的地址)。
這里之所以函數(shù)的名稱變成了這樣子,是因?yàn)槭褂玫木幾g器默認(rèn)使用C++方式進(jìn)行編譯,由于C++支持重載,那么需要給函數(shù)名增加額外的符號(hào),來(lái)使與同名的重載函數(shù)區(qū)分開來(lái),才能在DLL中通過(guò)符號(hào)名來(lái)進(jìn)行定位。
這里可以做個(gè)簡(jiǎn)單的測(cè)試,新建控制臺(tái)測(cè)試工程DllTest如下。
復(fù)制代碼 代碼如下:

// DllTest工程,DllTest.cpp
#include <iostream>
using namespace std;
int main(void)
{
// extern int add(int a, int b);
// _declspec(dllimport)是導(dǎo)入聲明,這種方式比上面的方式更有效,同時(shí)編譯器能邊編譯出更加高效的代碼。
_declspec(dllimport) int add(int a, int b);
cout << add(1,2) << endl;
getchar();
return 0;
}

編譯鏈接,提示鏈接錯(cuò)誤 error LNK2019: unresolved external symbol "__declspec(dllimport) int cdecl add(int,int)" (__imp_?add@@YAHHH@Z) referenced in function _main,很明顯的編譯器在編譯的時(shí)候,把a(bǔ)dd函數(shù)也給重命名了,并且和上面用depends查看的一樣。意思是沒(méi)有找到這個(gè)符號(hào)的定義。

添加代碼后如下:(注意,我這里兩個(gè)工程的輸出目錄都是在和解決方案同目錄的debug下,為了避免每次修改都重新拷貝lib文件,直接使用相對(duì)路徑聲明。)
復(fù)制代碼 代碼如下:

// DllTest工程,DllTest.cpp
#include <iostream>
using namespace std;
#pragma comment(lib, "../debug/dll1.lib") // 顯示的聲明要鏈接dll1.lib,隱式調(diào)用
int main(void)
{
// extern int add(int a, int b);
// _declspec(dllimport)是導(dǎo)入聲明,這種方式比上面的方式更有效,同時(shí)編譯器能邊編譯出更加高效的代碼。
_declspec(dllimport) int add(int a, int b);
cout << add(1,2) << endl;
getchar();
return 0;
}

編譯運(yùn)行后,使用depends工具對(duì)DllTest.exe查看其依賴的輸入信息如下:
image 
可以看出,DllTest.exe通過(guò)dll1.lib,引入了對(duì)dll1.dll的依賴。

--------------------------------------------------------------------------------

DLL提供的頭文件
通常情況下,當(dāng)?shù)玫揭粋€(gè).dll的時(shí)候,我們無(wú)法得知其提供了哪些函數(shù)調(diào)用(準(zhǔn)確來(lái)說(shuō),應(yīng)該是調(diào)用方式。因?yàn)槲覀兛梢岳胐epends工具查看dll導(dǎo)出的函數(shù)及其序號(hào),當(dāng)然也許可能有其他的方式去知道具體怎么使用,但是肯定無(wú)法得知內(nèi)部具體實(shí)現(xiàn)細(xì)節(jié)。),因此為了方便被使用,通常會(huì)提供一個(gè)對(duì)應(yīng)該dll的.h文件,來(lái)聲明其提供給客戶端使用的方式和說(shuō)明等信息??蛻舳耸褂迷擃^文件對(duì)所使用的接口進(jìn)行導(dǎo)入。但是為了避免很多地方都出現(xiàn)這些函數(shù)的聲明,通常在客戶端直接在.h文件中對(duì)所有接口進(jìn)行導(dǎo)入,而在Dll編譯時(shí),則作為導(dǎo)出使用。方法如下:
復(fù)制代碼 代碼如下:

// DLL1工程,dll1.h
#ifndef DLL1_API
#define DLL1_API _declspec(dllimport)
#endif
// 以上代碼表示,如果在包含該頭文件之前,沒(méi)有定義DLL1_API宏,那么后面所有DLL1_API宏都展開為_declspec(dllimport),即導(dǎo)入。
// 因?yàn)橥ǔG闆r下客戶端不會(huì)去定義這個(gè)宏(當(dāng)然,假設(shè)這個(gè)宏不會(huì)被客戶端中其他文件定義),所以客戶端使用該頭文件的時(shí)候,都是用于導(dǎo)入。
DLL1_API int add(int a, int b);

復(fù)制代碼 代碼如下:

// DLL1工程,dll1.cpp
#define DLL1_API _declspec(dllexport)
// 注意上面這行,在頭文件被包含前,先定義了DLL1_API這個(gè)宏,使得頭文件中DLL1_API都被展開為_declspec(dllexport)了,從而聲明函數(shù)作為導(dǎo)出。
#include "dll1.h"
// 在頭文件中進(jìn)行了導(dǎo)出聲明的函數(shù),就不用再聲明導(dǎo)出了。
int add(int a, int b)
{
return a + b;
}

相應(yīng)的,TestDll工程中包含.h文件后,也不用再去申明了。
復(fù)制代碼 代碼如下:

// DllTest工程,DllTest.cpp
#include <iostream>
using namespace std;
#include "../dll1/dll1.h" // 包含該頭文件之后,后面就不需要再申明了
#pragma comment(lib, "../debug/dll1.lib") // 顯示的聲明要鏈接dll1.lib,隱式調(diào)用
int main(void)
{
cout << add(1,2) << endl;
getchar();
return 0;
}

以上基本解釋了為什么通常引用dll的時(shí)候都有一個(gè)頭文件,并且頭文件內(nèi)有很多#ifndef之類的東東了。

--------------------------------------------------------------------------------
動(dòng)態(tài)鏈接庫(kù)導(dǎo)出類
當(dāng)然,動(dòng)態(tài)鏈接庫(kù)也能導(dǎo)出類,要注意的是聲明的方式為class DLL1_API CSample,而不是DLL1_API class CSample。
同時(shí),要注意導(dǎo)出類的同時(shí),其所有成員函數(shù)也已經(jīng)導(dǎo)出,但是仍然遵循類成員變量訪問(wèn)權(quán)限限制。
如果單獨(dú)導(dǎo)出類的成員函數(shù)(聲明方式和全局函數(shù)一樣),那么在客戶端可以實(shí)例化類對(duì)象,并調(diào)用導(dǎo)出的成員函數(shù),不能調(diào)用沒(méi)導(dǎo)出的成員函數(shù)(即使是public的)。

--------------------------------------------------------------------------------
改編了的符號(hào)名
在導(dǎo)出符號(hào)時(shí),講過(guò)C++會(huì)對(duì)函數(shù)名進(jìn)行改編,以支持函數(shù)重載。那么就會(huì)存在一個(gè)問(wèn)題,如果使用不用的C++編譯器(導(dǎo)致編譯出的符號(hào)名不同)或者客戶端使用C編譯器調(diào)用,就會(huì)出現(xiàn)LNK2019這樣的鏈接錯(cuò)誤,找不到符號(hào)。這個(gè)問(wèn)題很大的限制了DLL的使用范圍。
解決方法1:
使用extern “C”(注意這個(gè)C一定要大寫)前置申明,表明函數(shù)是以C的方式編譯鏈接的。C方式編譯連接導(dǎo)出的函數(shù)不會(huì)改編符號(hào)名,因而可以避免上述問(wèn)題。
復(fù)制代碼 代碼如下:

// DLL1工程,dll1.h
#ifndef DLL1_API
#define DLL1_API extern "C" _declspec(dllimport)
#endif
// 以上代碼表示,如果在包含該頭文件之前,沒(méi)有定義DLL1_API宏,那么后面所有DLL1_API宏都展開為_declspec(dllimport),即導(dǎo)入。
// 因?yàn)橥ǔG闆r下客戶端不會(huì)去定義這個(gè)宏(當(dāng)然,假設(shè)這個(gè)宏不會(huì)被客戶端中其他文件定義),所以客戶端使用該頭文件的時(shí)候,都是用于導(dǎo)入。
DLL1_API int add(int a, int b);
//class CSample
//{
//public:
// DLL1_API int substract(int a,int b);// 這種情況下,導(dǎo)出類成員函數(shù),編譯不能通過(guò)的
//};

這里要注意.h和.cpp中都要加上extern “C”,使得導(dǎo)入和導(dǎo)出都用C編譯方式。
復(fù)制代碼 代碼如下:

// DLL1工程,dll1.cpp
#define DLL1_API extern "C" _declspec(dllexport)
// 注意上面這行,在頭文件被包含前,先定義了DLL1_API這個(gè)宏,使得頭文件中DLL1_API都被展開為_declspec(dllexport)了,從而聲明函數(shù)作為導(dǎo)出。
#include "dll1.h"
// 在頭文件中進(jìn)行了導(dǎo)出聲明的函數(shù),就不用再聲明導(dǎo)出了。
int add(int a, int b)
{
return a + b;
}
//
//int CSample::substract(int a,int b)
//{
// return a - b;
//}

然后用depends進(jìn)行查看:
image 
導(dǎo)出的函數(shù)符號(hào)名和函數(shù)聲明時(shí)一樣了。由于客戶端使用的時(shí)候,導(dǎo)入也是用的extern “C”方式,因此客戶端在編譯鏈接的時(shí)候,也使用的是函數(shù)原名稱符號(hào)。
顯示,由于使用的是C編譯鏈接方式,C++的類和成員函數(shù)的導(dǎo)出就不能用這種方式了。
此外,如果我們?cè)诮o函數(shù)聲明加上標(biāo)準(zhǔn)調(diào)用約定:DLL1_API int _stdcall add(int a, int b);(注意,在函數(shù)的定義中也要加上_stdcall)。那么編譯出來(lái)的結(jié)果使用depends查看符號(hào)名為_add@8,也就是說(shuō)符號(hào)名稱又改了。
解決方法2:
使用模塊定義文件.def,這種文件的格式規(guī)范查看MSDN,搜索.def即可。其中LIBRARY命令用于指名該def文件用于導(dǎo)出庫(kù)文件,EXPORTS用于指名導(dǎo)出函數(shù)符號(hào)名。也就是說(shuō),.def文件主要就用于控制導(dǎo)出符號(hào)等信息。
復(fù)制代碼 代碼如下:

LIBRARY dll1
EXPORTS
add11=add

我這里給add函數(shù)別名為add11,注意同時(shí)需要在.h文件中聲明add為add11(作用就是提供給客戶端使用,當(dāng)然其實(shí)也可以直接在客戶端聲明函數(shù)為add11,前提是你知道該函數(shù)的定義方式等)。一旦提供了.def之后,.cpp中提供的任何調(diào)用約定都不再生效,因?yàn)?def指定了生成的符號(hào)名了。這里只要明白,.def控制了Dll的導(dǎo)出符號(hào),客戶端使用的時(shí)候,只要提供了聲明,并且鏈接上.lib文件,就能夠使用了。
補(bǔ)充一句:VC6.0以后的IDE都需要在鏈接選項(xiàng)(LINK)的input-》module define file中,指明.def文件,編譯器才會(huì)去使用這個(gè).def文件。

備注:
這一塊關(guān)于調(diào)用約定,以及加不加extern “C”,目前我還比較混亂。等了解了之后,再來(lái)這里進(jìn)行補(bǔ)充,歡迎看客給我提供有關(guān)這一塊比較好的介紹資料。

--------------------------------------------------------------------------------
顯示鏈接(動(dòng)態(tài)導(dǎo)入鏈接)
前面有提到讓導(dǎo)出的函數(shù)沒(méi)有名字,那么客戶端如何對(duì)其進(jìn)行調(diào)用呢。就是使用符號(hào)表中的ordinal了(可以通過(guò)工具進(jìn)行查看),當(dāng)然也可以使用函數(shù)名進(jìn)行導(dǎo)入。動(dòng)態(tài)導(dǎo)入不需要lib文件和.h文件(如果知道函數(shù)名的話)。
復(fù)制代碼 代碼如下:

// DllTest工程,DllTest.cpp
#include <iostream>
using namespace std;
#include <windows.h>
int main(void)
{
HMODULE hModule = ::LoadLibraryA("dll1.dll");
if(NULL != hModule)
{
typedef int (*ADDPROC)(int a, int b);
//ADDPROC add = (ADDPROC)::GetProcAddress(hModule, "add11"); // 通過(guò)函數(shù)名導(dǎo)入
ADDPROC add = (ADDPROC)::GetProcAddress(hModule, MAKEINTRESOURCEA(1)); // 通過(guò)ordinal導(dǎo)入,注意第二個(gè)參數(shù)此時(shí)數(shù)值要放在低字節(jié)位置,具體參見MSDN說(shuō)明。
if(NULL != add)
cout << add(1,1) << endl;
::FreeLibrary(hModule);
}
getchar();
return 0;
}

值得一提的是,根據(jù)函數(shù)名稱進(jìn)行動(dòng)態(tài)導(dǎo)入的時(shí)候,實(shí)際上也是根據(jù)符號(hào)名的,也就是說(shuō),dll提供的符號(hào)名要和提供給用戶的函數(shù)聲明一致。

此外,動(dòng)態(tài)導(dǎo)入動(dòng)態(tài)鏈接庫(kù)時(shí),.exe時(shí)無(wú)法查看到需要的輸入信息的。

--------------------------------------------------------------------------------
后記
第一次寫這么詳細(xì)和這么長(zhǎng)的博文,可以看出來(lái)寫到后面的時(shí)候已經(jīng)越來(lái)越粗糙了。因?yàn)閷懙胶髞?lái)的時(shí)候,自己都已經(jīng)不知道要寫什么了,因?yàn)槿绻贁U(kuò)展下去,需要增加的東西就太多了。同時(shí)也有一部分原因是后面的內(nèi)容我理解的不是太透徹,同時(shí)使用的也很少??傊@會(huì)我腦子已經(jīng)相當(dāng)混亂了。在這里對(duì)那些寫博客的大俠們表示深深的敬佩。

最后要申明的是,這些都是我自己參考視頻整理總結(jié)出來(lái)的,肯定有不專業(yè)甚至不對(duì)的地方,懇請(qǐng)?zhí)岢鲋笇?dǎo),我在學(xué)習(xí)理解之后,會(huì)及時(shí)進(jìn)行修改,謝謝。

相關(guān)文章

最新評(píng)論