关于VS环境下制作和使用静态库和动态库
轉(zhuǎn)載理由:雖然操作什么的很基礎(chǔ),不過(guò)作為初學(xué)還是很好的文章,手把手教學(xué)
關(guān)于VS2013下制作和使用靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
引言
- 什么是庫(kù):庫(kù)是寫(xiě)好的現(xiàn)有的,成熟的,可以復(fù)用的代碼。
- 所謂靜態(tài)、動(dòng)態(tài)是指鏈接。將一個(gè)程序編譯成可執(zhí)行程序的步驟:
靜態(tài)庫(kù)在鏈接階段,會(huì)將匯編生成的目標(biāo)文件.o與引用到的庫(kù)一起鏈接打包到可執(zhí)行文件中。因此對(duì)應(yīng)的鏈接方式稱(chēng)為靜態(tài)鏈接。
為什么還需要?jiǎng)討B(tài)庫(kù)?
- 空間浪費(fèi)是靜態(tài)庫(kù)的一個(gè)問(wèn)題。
- 另一個(gè)問(wèn)題是靜態(tài)庫(kù)對(duì)程序的更新、部署帶來(lái)麻煩。如果靜態(tài)庫(kù)liba.lib更新了,所以使用它的應(yīng)用程序都需要重新編譯、發(fā)布給用戶(hù)(對(duì)于玩家來(lái)說(shuō),可能是一個(gè)很小的改動(dòng),卻導(dǎo)致整個(gè)程序重新下載,全量更新)。
- 動(dòng)態(tài)庫(kù)在程序編譯時(shí)并不會(huì)被連接到目標(biāo)代碼中,而是在程序運(yùn)行是才被載入。不同的應(yīng)用程序如果調(diào)用相同的庫(kù),那么在內(nèi)存里只需要有一份該共享庫(kù)的實(shí)例,規(guī)避了空間浪費(fèi)問(wèn)題。動(dòng)態(tài)庫(kù)在程序運(yùn)行時(shí)才會(huì)載入,也解決了靜態(tài)庫(kù)對(duì)程序的更新、部署帶來(lái)麻煩。用戶(hù)只需要更新動(dòng)態(tài)庫(kù)即可,增量更新。
靜態(tài)庫(kù)
動(dòng)態(tài)庫(kù)
VS2013下新建靜態(tài)庫(kù)
第一步:新建一個(gè)靜態(tài)庫(kù)(static library)項(xiàng)目
第二步:編寫(xiě)項(xiàng)目?jī)?nèi)容
首先在頭文件下添加一個(gè)頭文件static.h
頭文件static.h的內(nèi)容如下:
在源文件下添加一個(gè)源文件static.cpp,內(nèi)容如下:
int sum(int x, int y){ return x + y;}第三步:生成.lib文件
在菜單欄選擇“生成”->“生成解決方案”就可以了。
然后打開(kāi)工程文件夾,在Debug目錄下(編譯選擇的是默認(rèn)的Debug和Win32)就可以看到一個(gè)和項(xiàng)目名稱(chēng)相同的lib文件:
這樣供給別人調(diào)用的lib文件就生成好了,下面講如何調(diào)用這個(gè)靜態(tài)庫(kù)文件。
第四步:調(diào)用.lib文件
首先新建另外的項(xiàng)目TestCallLib1,這個(gè)項(xiàng)目將使用我們剛剛生成的靜態(tài)庫(kù)
將頭文件static.h和靜態(tài)庫(kù)TestLib1.lib拷到TestCallLib1\TestCallLib1目錄下
首先將主函數(shù)寫(xiě)好
有兩種方法調(diào)用靜態(tài)庫(kù):
1、右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴(lài)項(xiàng)”這里添加要調(diào)用的lib文件的名字:TestLib1.lib。
2、或者編寫(xiě)代碼為
運(yùn)行得到結(jié)果!
動(dòng)態(tài)庫(kù)的制作
創(chuàng)建動(dòng)態(tài)庫(kù)關(guān)鍵是導(dǎo)出函數(shù),DLL中導(dǎo)出函數(shù)的聲明有兩種方式:
- 一種方式是:在函數(shù)聲明中加上__declspec(dllexport);
- 另外一種方式是:采用模塊定義(.def)文件聲明,(.def)文件為鏈接器提供了有關(guān)被鏈接程序的導(dǎo)出、屬性及其他方面的信息。
1、采用模塊定義(.def)文件聲明
新建項(xiàng)目win32,應(yīng)用程序類(lèi)型選擇dll
需要自己手動(dòng)添加四個(gè)文件
頭文件dllgenerator.h,內(nèi)容:
int Add(int, int);int Mul(int, int);函數(shù)定義文件dllgenerator.cpp,內(nèi)容:
int Add(int a, int b){return a + b;} int Mul(int c, int d){return c * d;}dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)
// dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)。 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) {case DLL_PROCESS_ATTACH:case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH:break; } return TRUE; }需包含庫(kù)
建立源文件Source.def(劃重點(diǎn))
在此文件第一行引號(hào)中填你的項(xiàng)目名稱(chēng)即可,EXPORTS下面列出要生成的函數(shù)名稱(chēng) 格式:“名稱(chēng) @序號(hào)”)
LIBRARY "TestDll1" EXPORTS Add @1 Mul @2點(diǎn)擊菜單欄 生成 -> 生成解決方案,我們的動(dòng)態(tài)庫(kù)就生成成功了
2、通過(guò)關(guān)鍵字導(dǎo)出函數(shù)
新建項(xiàng)目過(guò)程同上!這里的關(guān)鍵是頭文件中需要加入關(guān)鍵字__declspec(dllexport),這個(gè)關(guān)鍵字是導(dǎo)出函數(shù)的關(guān)鍵!
添加lib.h和lib.cpp函數(shù)
生成后同樣看見(jiàn).dll文件和.lib文件
動(dòng)態(tài)庫(kù)的加載——靜態(tài)加載
新建項(xiàng)目DLLTestor,將剛剛生成的.dll、.lib、.h文件拷貝到DLLTestor\DLLTestor文件下( .lib、.h 文件不是必須拷入的,我這里并沒(méi)有拷入)
工程調(diào)用dll時(shí)首先在工程文件目錄中查找dll,找不到后在C:\Windows\System32 中找。所以我們自己項(xiàng)目簡(jiǎn)單調(diào)用dll時(shí)就把生成的dll文件復(fù)制到工程目錄中,如果經(jīng)常用可以把dll文件放到C:\Windows\System32中
調(diào)用動(dòng)態(tài)庫(kù)是調(diào)用 .dll、 .lib、 .h 三個(gè)文件
編好代碼并設(shè)置.lib和.h文件的調(diào)用路徑
右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴(lài)項(xiàng)”這里添加要調(diào)用的lib文件的名字:TestDll1.lib
運(yùn)行得到結(jié)果
動(dòng)態(tài)加載和靜態(tài)加載
動(dòng)態(tài)加載是指在生成可執(zhí)行文件時(shí)不將所有程序用到的函數(shù)鏈接到一個(gè)文件,因?yàn)橛性S多函數(shù)在操作系統(tǒng)帶的dll文件中,當(dāng)程序運(yùn)行時(shí)直接從操作系統(tǒng)中找。?
而靜態(tài)加載就是把所有用到的函數(shù)全部鏈接到exe文件中。動(dòng)態(tài)加載是只建立一個(gè)引用的接口,而真正的代碼和數(shù)據(jù)存放在另外的可執(zhí)行模塊中,在運(yùn)行時(shí)再裝入;?
?
而靜態(tài)加載是把所有的代碼和數(shù)據(jù)都復(fù)制到本模塊中,運(yùn)行時(shí)就不再需要庫(kù)了。
動(dòng)態(tài)加載
int _tmain(int argc, CHAR* argv[]){ printf("Hello World!\n"); HMODULE hmod = LoadLibrary("TestDll1.dll"); //? //用于加載dll typedef int(*LoadProc)(int x, int y); LoadProc Load_proc = (LoadProc)GetProcAddress(hmod, "Add"); //GetProcAddress()用于獲得函數(shù)地址 int iRet = Load_proc(3, 5); //得到地址后調(diào)用該函數(shù),返回較大值 printf("the Add the value is:%x\n", iRet); return 0; }需要理解調(diào)用動(dòng)態(tài)庫(kù)主要用到三個(gè)函數(shù),加載LoadLibrary、調(diào)用GetProcAddress、釋放FreeLibrary!
別忘了在右鍵“目錄”“屬性”選擇“鏈接器”->“輸入”,在“附加依賴(lài)項(xiàng)”這里添加要調(diào)用的lib文件的名字:TestDll1.lib
大家在制作過(guò)程中可能會(huì)遇到如下的問(wèn)題
這是字符集的問(wèn)題,具體的解決方案是:右鍵項(xiàng)目->屬性
把字符集從unicode改成多字節(jié)字符集。
h頭文件 .lib庫(kù)文件 .dll動(dòng)態(tài)鏈接庫(kù)文件關(guān)系
- .h頭文件是編譯時(shí)必須的,lib是鏈接時(shí)需要的,dll是運(yùn)行時(shí)需要的。
附加依賴(lài)項(xiàng)添加的是.lib而不是.dll,若生成了DLL,則肯定也生成了LIB文件。 - H文件的作用:聲明函數(shù)接口
- DLL文件作用:函數(shù)可執(zhí)行代碼
- LIB文件作用:當(dāng)我們?cè)谧约旱某绦蛑幸昧艘粋€(gè)H文件里的函數(shù),鏈接器怎么知道該調(diào)用哪個(gè)DLL文件呢?這就是LIB文件的作用了。它告訴鏈接器調(diào)用的函數(shù)在哪個(gè)DLL中,函數(shù)執(zhí)行代碼在DLL中的什么位置,這也就是為什么需要附加依賴(lài)項(xiàng).LIB文件,它起到橋梁的作用。
- 如果是生成靜態(tài)庫(kù)文件,則沒(méi)有DLL,只有l(wèi)ib,這時(shí)函數(shù)可執(zhí)行代碼部分也在lib文件中。
再說(shuō)一點(diǎn)
目前以lib后綴的庫(kù)有兩種,一種為靜態(tài)鏈接庫(kù)(Static Libary,以下簡(jiǎn)稱(chēng)“靜態(tài)庫(kù)”),另一種為動(dòng)態(tài)連接庫(kù)(DLL,以下簡(jiǎn)稱(chēng)“動(dòng)態(tài)庫(kù)”)的導(dǎo)入庫(kù)(Import Libary,以下簡(jiǎn)稱(chēng)“導(dǎo)入庫(kù)”)。靜態(tài)庫(kù)是一個(gè)或者多個(gè)obj文件的打包,所以有人干脆把從obj文件生成lib的過(guò)程稱(chēng)為Archive,即合并到一起。比如你鏈接一個(gè)靜態(tài)庫(kù),如果其中有錯(cuò),它會(huì)準(zhǔn)確的找到是哪個(gè)obj有錯(cuò),即靜態(tài)lib只是殼子。動(dòng)態(tài)庫(kù)一般會(huì)有對(duì)應(yīng)的導(dǎo)入庫(kù),方便程序靜態(tài)載入動(dòng)態(tài)鏈接庫(kù),否則你可能就需要自己LoadLibary調(diào)入DLL文件,然后再手工GetProcAddress獲得對(duì)應(yīng)函數(shù)了。有了導(dǎo)入庫(kù),你只需要鏈接導(dǎo)入庫(kù)后按照頭文件函數(shù)接口的聲明調(diào)用函數(shù)就可以了。導(dǎo)入庫(kù)和靜態(tài)庫(kù)的區(qū)別很大,他們實(shí)質(zhì)是不一樣的東西。靜態(tài)庫(kù)本身就包含了實(shí)際執(zhí)行代碼、符號(hào)表等等,而對(duì)于導(dǎo)入庫(kù)而言,其實(shí)際的執(zhí)行代碼位于動(dòng)態(tài)庫(kù)中,導(dǎo)入庫(kù)只包含了地址符號(hào)表等,確保程序找到對(duì)應(yīng)函數(shù)的一些基本地址信息。
一般的動(dòng)態(tài)庫(kù)程序有l(wèi)ib文件和dll文件。lib文件是必須在編譯期就連接到應(yīng)用程序中的,而dll文件是運(yùn)行期才會(huì)被調(diào)用的。如果有dll文件,那么對(duì)應(yīng)的lib文件一般是一些索引信息,具體的實(shí)現(xiàn)在dll文件中。如果只有l(wèi)ib文件,那么這個(gè)lib文件是靜態(tài)編譯出來(lái)的,索引和實(shí)現(xiàn)都在其中。
總結(jié)
以上是生活随笔為你收集整理的关于VS环境下制作和使用静态库和动态库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 修改注册表实现程序开机自启动
- 下一篇: i386和X86各是什么意思