windows中,鏈接庫分為兩種類型:靜態(tài)鏈接庫.lib和動態(tài)鏈接庫.dll。其中動態(tài)鏈接庫在被使用的時候,通常還提供一個.lib,稱為引入庫,它主要提供被Dll導(dǎo)出的函數(shù)和符號名稱,使得鏈接的時候能夠找到dll中對應(yīng)的函數(shù)映射。
靜態(tài)鏈接庫和動態(tài)鏈接庫的作用相似,都是提供給其他程序進行調(diào)用的資源。其中,動態(tài)鏈接庫的調(diào)用方法分隱式調(diào)用(靜態(tài)導(dǎo)入調(diào)用)和顯示調(diào)用(動態(tài)導(dǎo)入調(diào)用)。?
編譯環(huán)境:
Microsoft Visual Stdio 2010
--------------------------------------------------------------------------------
DLL導(dǎo)出符號
例,首先生成一個dll1.dll和dll1.lib
利用微軟的depends工具查看dll1.dll,導(dǎo)出的符號如下:
?
其中各字段意義:Ordinal(符號序號,后面使用GetProcAddress的時候,參考的數(shù)值),Hint(這個我也不是太明白,據(jù)說是不用了解),F(xiàn)unction(這個就是函數(shù)導(dǎo)出后的符號名稱了),EntryPoint(這個是函數(shù)在DLL中的地址)。
這里之所以函數(shù)的名稱變成了這樣子,是因為使用的編譯器默認(rèn)使用C++方式進行編譯,由于C++支持重載,那么需要給函數(shù)名增加額外的符號,來使與同名的重載函數(shù)區(qū)分開來,才能在DLL中通過符號名來進行定位。
這里可以做個簡單的測試,新建控制臺測試工程DllTest如下。
?
?
編譯鏈接,提示鏈接錯誤 error LNK2019: unresolved external symbol "__declspec(dllimport) int cdecl add(int,int)" (__imp_?add@@YAHHH@Z) referenced in function _main,很明顯的編譯器在編譯的時候,把add函數(shù)也給重命名了,并且和上面用depends查看的一樣。意思是沒有找到這個符號的定義。
添加代碼后如下:(注意,我這里兩個工程的輸出目錄都是在和解決方案同目錄的debug下,為了避免每次修改都重新拷貝lib文件,直接使用相對路徑聲明。)
?
?
編譯運行后,使用depends工具對DllTest.exe查看其依賴的輸入信息如下:
?
可以看出,DllTest.exe通過dll1.lib,引入了對dll1.dll的依賴。
--------------------------------------------------------------------------------
DLL提供的頭文件
通常情況下,當(dāng)?shù)玫揭粋€.dll的時候,我們無法得知其提供了哪些函數(shù)調(diào)用(準(zhǔn)確來說,應(yīng)該是調(diào)用方式。因為我們可以利用depends工具查看dll導(dǎo)出的函數(shù)及其序號,當(dāng)然也許可能有其他的方式去知道具體怎么使用,但是肯定無法得知內(nèi)部具體實現(xiàn)細節(jié)。),因此為了方便被使用,通常會提供一個對應(yīng)該dll的.h文件,來聲明其提供給客戶端使用的方式和說明等信息??蛻舳耸褂迷擃^文件對所使用的接口進行導(dǎo)入。但是為了避免很多地方都出現(xiàn)這些函數(shù)的聲明,通常在客戶端直接在.h文件中對所有接口進行導(dǎo)入,而在Dll編譯時,則作為導(dǎo)出使用。方法如下:
?
?
?
?
相應(yīng)的,TestDll工程中包含.h文件后,也不用再去申明了。
?
?
以上基本解釋了為什么通常引用dll的時候都有一個頭文件,并且頭文件內(nèi)有很多#ifndef之類的東東了。
--------------------------------------------------------------------------------
動態(tài)鏈接庫導(dǎo)出類
當(dāng)然,動態(tài)鏈接庫也能導(dǎo)出類,要注意的是聲明的方式為class DLL1_API CSample,而不是DLL1_API class CSample。
同時,要注意導(dǎo)出類的同時,其所有成員函數(shù)也已經(jīng)導(dǎo)出,但是仍然遵循類成員變量訪問權(quán)限限制。
如果單獨導(dǎo)出類的成員函數(shù)(聲明方式和全局函數(shù)一樣),那么在客戶端可以實例化類對象,并調(diào)用導(dǎo)出的成員函數(shù),不能調(diào)用沒導(dǎo)出的成員函數(shù)(即使是public的)。
--------------------------------------------------------------------------------
改編了的符號名
在導(dǎo)出符號時,講過C++會對函數(shù)名進行改編,以支持函數(shù)重載。那么就會存在一個問題,如果使用不用的C++編譯器(導(dǎo)致編譯出的符號名不同)或者客戶端使用C編譯器調(diào)用,就會出現(xiàn)LNK2019這樣的鏈接錯誤,找不到符號。這個問題很大的限制了DLL的使用范圍。
解決方法1:
使用extern “C”(注意這個C一定要大寫)前置申明,表明函數(shù)是以C的方式編譯鏈接的。C方式編譯連接導(dǎo)出的函數(shù)不會改編符號名,因而可以避免上述問題。
?
?
這里要注意.h和.cpp中都要加上extern “C”,使得導(dǎo)入和導(dǎo)出都用C編譯方式。
?
?
然后用depends進行查看:
?
導(dǎo)出的函數(shù)符號名和函數(shù)聲明時一樣了。由于客戶端使用的時候,導(dǎo)入也是用的extern “C”方式,因此客戶端在編譯鏈接的時候,也使用的是函數(shù)原名稱符號。
顯示,由于使用的是C編譯鏈接方式,C++的類和成員函數(shù)的導(dǎo)出就不能用這種方式了。
此外,如果我們在給函數(shù)聲明加上標(biāo)準(zhǔn)調(diào)用約定:DLL1_API int _stdcall add(int a, int b);(注意,在函數(shù)的定義中也要加上_stdcall)。那么編譯出來的結(jié)果使用depends查看符號名為_add@8,也就是說符號名稱又改了。
解決方法2:
使用模塊定義文件.def,這種文件的格式規(guī)范查看MSDN,搜索.def即可。其中LIBRARY命令用于指名該def文件用于導(dǎo)出庫文件,EXPORTS用于指名導(dǎo)出函數(shù)符號名。也就是說,.def文件主要就用于控制導(dǎo)出符號等信息。
?
?
我這里給add函數(shù)別名為add11,注意同時需要在.h文件中聲明add為add11(作用就是提供給客戶端使用,當(dāng)然其實也可以直接在客戶端聲明函數(shù)為add11,前提是你知道該函數(shù)的定義方式等)。一旦提供了.def之后,.cpp中提供的任何調(diào)用約定都不再生效,因為.def指定了生成的符號名了。這里只要明白,.def控制了Dll的導(dǎo)出符號,客戶端使用的時候,只要提供了聲明,并且鏈接上.lib文件,就能夠使用了。
補充一句:VC6.0以后的IDE都需要在鏈接選項(LINK)的input-》module define file中,指明.def文件,編譯器才會去使用這個.def文件。
備注:
這一塊關(guān)于調(diào)用約定,以及加不加extern “C”,目前我還比較混亂。等了解了之后,再來這里進行補充,歡迎看客給我提供有關(guān)這一塊比較好的介紹資料。
--------------------------------------------------------------------------------
顯示鏈接(動態(tài)導(dǎo)入鏈接)
前面有提到讓導(dǎo)出的函數(shù)沒有名字,那么客戶端如何對其進行調(diào)用呢。就是使用符號表中的ordinal了(可以通過工具進行查看),當(dāng)然也可以使用函數(shù)名進行導(dǎo)入。動態(tài)導(dǎo)入不需要lib文件和.h文件(如果知道函數(shù)名的話)。
?
?
值得一提的是,根據(jù)函數(shù)名稱進行動態(tài)導(dǎo)入的時候,實際上也是根據(jù)符號名的,也就是說,dll提供的符號名要和提供給用戶的函數(shù)聲明一致。
此外,動態(tài)導(dǎo)入動態(tài)鏈接庫時,.exe時無法查看到需要的輸入信息的。
--------------------------------------------------------------------------------
后記
第一次寫這么詳細和這么長的博文,可以看出來寫到后面的時候已經(jīng)越來越粗糙了。因為寫到后來的時候,自己都已經(jīng)不知道要寫什么了,因為如果再擴展下去,需要增加的東西就太多了。同時也有一部分原因是后面的內(nèi)容我理解的不是太透徹,同時使用的也很少??傊@會我腦子已經(jīng)相當(dāng)混亂了。在這里對那些寫博客的大俠們表示深深的敬佩。
以上就是鏈接庫動態(tài)鏈接庫詳細說明的介紹,希望小編整理的相關(guān)知識和資料都對你們有所幫助,更多內(nèi)容請繼續(xù)關(guān)注武林技術(shù)頻道網(wǎng)站!
?
新聞熱點
疑難解答
圖片精選