[TOC]
一. 簡(jiǎn)介
1.1 C++兩種庫(kù)文件
- lib包含了函數(shù)所在的dll文件和文件中函數(shù)位置的信息(入口),代碼由運(yùn)行時(shí)加載在進(jìn)程空間中的dll提供,稱為動(dòng)態(tài)鏈接庫(kù)dynamic link library。(這種方式更靈活,寫的程序體積小,但是需要.exe和dll同時(shí)發(fā)布)
- lib包含函數(shù)代碼本身,在編譯時(shí)直接將代碼加入程序當(dāng)中,稱為靜態(tài)鏈接庫(kù)static link library。(這種方式不是很靈活,因?yàn)閘ib被編譯到.exe中,寫出的程序體積大,但是只需要發(fā)布exe即可,不需要dll文件)
1.2 C++兩種鏈接方式
- 動(dòng)態(tài)鏈接使用動(dòng)態(tài)鏈接庫(kù),允許可執(zhí)行模塊(.dll文件或.exe文件)僅包含在運(yùn)行時(shí)定位 dll 函數(shù)的可執(zhí)行代碼所需的信息。
- 靜態(tài)鏈接使用靜態(tài)鏈接庫(kù),鏈接器從靜態(tài)鏈接庫(kù) lib 獲取所有被引用函數(shù),并將庫(kù)同代碼一起放到可執(zhí)行文件中。
1.3 lib與dll的區(qū)別
1. 功能區(qū)別
- lib是編譯時(shí)用到的,dll是運(yùn)行時(shí)用到的。如果要完成源代碼的編譯,只需要lib;如果要使動(dòng)態(tài)鏈接的程序運(yùn)行起來,只需要dll。
- 如果有dll文件,那么lib一般是一些索引信息,記錄了dll中函數(shù)的入口和位置,dll中是函數(shù)的具體內(nèi)容;如果只有l(wèi)ib文件,那么這個(gè)lib文件是靜態(tài)編譯出來的,索引和實(shí)現(xiàn)都在其中。使用靜態(tài)編譯的lib文件,在運(yùn)行程序時(shí)不需要再掛動(dòng)態(tài)庫(kù),缺點(diǎn)是導(dǎo)致應(yīng)用程序比較大,而且失去了動(dòng)態(tài)庫(kù)的靈活性,發(fā)布新版本時(shí)要發(fā)布新的應(yīng)用程序才行。
- 動(dòng)態(tài)鏈接的情況下,有兩個(gè)文件:一個(gè)是LIB文件,一個(gè)是DLL文件。LIB包含被DLL導(dǎo)出的函數(shù)名稱和位置,DLL包含實(shí)際的函數(shù)和數(shù)據(jù),應(yīng)用程序使用LIB文件鏈接到DLL文件。在應(yīng)用程序的可執(zhí)行文件中,存放的不是被調(diào)用的函數(shù)代碼,而是DLL中相應(yīng)函數(shù)代碼的地址,從而節(jié)省了內(nèi)存資源。DLL和LIB文件必須隨應(yīng)用程序一起發(fā)行,否則應(yīng)用程序會(huì)產(chǎn)生錯(cuò)誤。如果不想用lib文件或者沒有l(wèi)ib文件,可以用WIN32 API函數(shù)LoadLibrary、GetProcAddress裝載。
2. 文件數(shù)量的區(qū)別
- (靜態(tài)連接)使用lib需注意兩個(gè)文件:
- .h頭文件,包含lib中說明輸出的類或符號(hào)原型或數(shù)據(jù)結(jié)構(gòu)。應(yīng)用程序調(diào)用lib時(shí),需要將該文件包含入應(yīng)用程序的源文件中。
- .LIB文件,略。
- (動(dòng)態(tài)連接)使用dll需注意三個(gè)文件:
- .h頭文件,包含dll中說明輸出的類或符號(hào)原型或數(shù)據(jù)結(jié)構(gòu)的.h文件。應(yīng)用程序調(diào)用dll時(shí),需要將該文件包含入應(yīng)用程序的源文件中。
- .LIB文件,是dll在編譯、鏈接成功之后生成的文件,作用是當(dāng)其他應(yīng)用程序調(diào)用dll時(shí),需要將該文件引入應(yīng)用程序,否則產(chǎn)生錯(cuò)誤。如果不想用lib文件或者沒有l(wèi)ib文件,可以用WIN32API函數(shù)LoadLibrary、GetProcAddress裝載。
- .dll文件,真正的可執(zhí)行文件,開發(fā)成功后的應(yīng)用程序在發(fā)布時(shí),只需要有.exe文件和.dll文件,并不需要.lib文件和.h頭文件。
二. lib文件
2.1 生成工具
操作系統(tǒng): Win7
開發(fā)軟件: VS2010
2.2 生成步驟
建立win32控制臺(tái)工程MyLib(或者win32項(xiàng)目中下的靜態(tài)庫(kù)), 添加mySub.h文件以及mySub.cpp文件。
-
編寫mySub.h文件代碼
#ifndef _MYSUB_H // 這里的#ifndef可以避免頭文件重復(fù)包含 #define _MYSUB_H void mySub(int a,int b); // 這一行代碼不能夠?qū)懺谏弦恍?,只能另起一行?#endif -
編寫mySub.cpp文件代碼
#include "mySub.h" //包含頭文件 #include <iostream> void mySub(int a,int b) //自定義的函數(shù) { std::cout<<(a-b)<<std::endl; } -
由于在工程中,沒有main()函數(shù),所以編譯可能會(huì)出錯(cuò)。這時(shí),點(diǎn)擊工程,并選擇工程屬性,出現(xiàn)下圖,選擇靜態(tài)鏈接庫(kù)即可。
012302.png
- 這時(shí)候再按快捷鍵 F7,build solution即可產(chǎn)生lib文件。在Debug中只生成.lib文件。
2.3 lib文件的使用
-
新建一個(gè).cpp文件myLibTest.cpp(用于測(cè)試)
#include <iostream> #include "mySub.h" // 引用頭文件 using namespace std; #pragma comment(lib,"MyLib.lib") // 導(dǎo)入上一步生成的lib文件 int main() { mySub(5,4); // 調(diào)用lib中的自定義函數(shù)mySub() return 0; } -
點(diǎn)擊工程,并選擇工程屬性,出現(xiàn)下圖,將附加庫(kù)目錄新增包含剛才生成.lib的目錄。
012303.png
- 將工程項(xiàng)目屬性中的配置類型改回至原來默認(rèn)的應(yīng)用程序(.exe),并執(zhí)行myLibTest.cpp。
三. dll文件
3.1 生成.dll文件
新建win32項(xiàng)目,項(xiàng)目名稱為SubDLL,解決方案名稱為DLLTest,下一步。
選擇應(yīng)用程序類型為DLL,將附加選項(xiàng)的“導(dǎo)出符號(hào)”勾選上,完成。
-
修改SubDLL.h中的內(nèi)容(將原來代碼中,除預(yù)處理部分的代碼外全部刪除),并在后面新增你要實(shí)現(xiàn)的函數(shù)聲明(見代碼第21行)。注意:項(xiàng)目名為SubDLL,但此時(shí)生成的名字為 SUBDLL。
#ifdef SUBDLL_EXPORTS #define SUBDLL_API __declspec(dllexport) #else #define SUBDLL_API __declspec(dllimport) #endif /* // 此類是從 SubDLL.dll 導(dǎo)出的 class SUBDLL_API CSubDLL { public: CSubDLL(void); // TODO: 在此添加您的方法。 }; extern SUBDLL_API int nSubDLL; SUBDLL_API int fnSubDLL(void); */ //這邊是新增的內(nèi)容 SUBDLL_API void mySub(); -
修改SubDLL.cpp中的內(nèi)容(將原來代碼中,除頭文件引入部分的代碼外全部刪除),并在后面新增你要實(shí)現(xiàn)的函數(shù)聲明(見代碼第26行)。
// SubDLL.cpp : 定義 DLL 應(yīng)用程序的導(dǎo)出函數(shù)。 // #include "stdafx.h" #include "SubDLL.h" #include<stdio.h> /* // 這是導(dǎo)出變量的一個(gè)示例 SUBDLL_API int nSubDLL=0; // 這是導(dǎo)出函數(shù)的一個(gè)示例。 SUBDLL_API int fnSubDLL(void) { return 42; } // 這是已導(dǎo)出類的構(gòu)造函數(shù)。 // 有關(guān)類定義的信息,請(qǐng)參閱 SubDLL.h CSubDLL::CSubDLL() { return; } */ //這邊為SUM()的內(nèi)容,很簡(jiǎn)易 SUBDLL_API void mySub(int a,int b) { printf("the result is %d",a-b); } -
點(diǎn)擊“項(xiàng)目”,選擇“屬性”,進(jìn)行如下圖的配置(粗體字顯示部分)。
012306.png
- 構(gòu)建項(xiàng)目(build)/生成解決方案,在項(xiàng)目的debug目錄下面會(huì)生成很多的文件,其中包括有.dll和.lib。
3.2 dll文件的使用
3.2.1 顯示調(diào)用方式
在之前“解決方案”中新建項(xiàng)目(選中解決方案 -> 增加 -> 新建項(xiàng)目),這次選擇“win32控制臺(tái)應(yīng)用程序”,生成向?qū)е羞x擇“空項(xiàng)目”即可。取名為MyTest。
-
在新建項(xiàng)目的源文件下新建一個(gè)UseDLL.cpp文件,下面是其中的代碼。
#include <iostream> #include <Windows.h> //使用函數(shù)和某些特殊變量 using namespace std; typedef void (*FUN)(int,int); //定義一個(gè)函數(shù)指針,確定調(diào)用函數(shù)的形參 int main() { const char* dllname = "SUBDLL.dll"; // 加載.dll const char* funname = "mySub"; //SUMDLL.cpp中函數(shù)名稱 HMODULE hDLL = LoadLibrary(dllname); //不要問,跟著做 if (hDLL != NULL) { FUN fp = FUN(GetProcAddress(hDLL,funname)); //繼續(xù)做,不要問 if(fp != NULL) { fp(5,4); } else { cout << "Can not Find: " << funname << endl; } FreeLibrary(hDLL); } else cout << "Can not find: " << dllname; return 0; } 點(diǎn)擊解決方案名,選擇設(shè)置啟動(dòng)項(xiàng)目 -> 通用屬性 -> 啟動(dòng)項(xiàng)目 -> 單啟動(dòng)項(xiàng)目(選中UseDLL)。
-
運(yùn)行項(xiàng)目,出現(xiàn)了錯(cuò)誤:
Can not find:mySub。造成這種錯(cuò)誤的原因正是導(dǎo)出函數(shù)的修飾名稱。在dll二進(jìn)制文件中,經(jīng)過編譯器的“加工”,實(shí)際上有了不同的名稱。這也是函數(shù)重載機(jī)制得以實(shí)現(xiàn)的一個(gè)技術(shù)支持。怎么辦呢?我們可以通過vs2010附帶工具dumpbin,找到加工以后的名稱。詳見dumpbin工具的使用- 在C:\Program Files(x86)\Microsoft Visual Studio 10.0\VC\bin目錄下,按住shift鍵,鼠標(biāo)右鍵在空白處單擊,選擇在此處打開命令窗口
- 輸入命令: dumpbin /export 文件全名
- 將“加工”后的真是函數(shù)名復(fù)制后,粘貼。賦值給UseDLL.cpp文件中的變量funname。
- 經(jīng)過上一步后,重新執(zhí)行UseDLL.cpp,成功運(yùn)行。詳見VS2010 C++ 調(diào)用 DLL (C++編寫)
-
為了能夠使原來的UseDLL.cpp(上面第2步所示代碼)成功運(yùn)行,可以進(jìn)行下列操作:
-
在生成DLL文件的SubDLL項(xiàng)目的源文件中新建模塊定義文件createDLL.def,其中的代碼如下:
LIBRARY createDLL EXPORTS mySub = ?mySub@@YAXHH@Z //?mySub@@YAXHH@Z 即為dumpbin工具找到的真實(shí)名。 -
修改SubDLL.h中的代碼(去掉這些不太規(guī)范的修飾名稱),修改之后重新編譯生成CreateDLL.dll。
#ifdef SUBDLL_EXPORTS #define SUBDLL_API //去掉了原來的 __declspec(dllexport) #else //或改為 #define SUBDLL_API extern "C" __declspec(dllexport) #define SUBDLL_API //同上 #endif
-
重新運(yùn)行UseDLL.cpp程序,成功執(zhí)行。
3.2.2 隱式調(diào)用方式
在之前“解決方案”中新建項(xiàng)目(選中解決方案 -> 增加 -> 新建項(xiàng)目),這次選擇“win32控制臺(tái)應(yīng)用程序”,生成向?qū)е羞x擇“空項(xiàng)目”即可。取名為MyTest。
-
在新建項(xiàng)目的源文件下新建一個(gè)UseDLL.cpp文件,下面是其中的代碼。
#include <iostream> extern void mySub(int,int); int main() { mySub(5,4); return 0; } 右鍵工程–>Linker–>General–>Additional Library Directories(附加庫(kù)目錄) –>找到那個(gè)SubDLL.lib所在的目錄
右鍵UseDLL工程–>Linker->input寫下lib的名稱。如SubDLL.lib和你DEBUG文件下的對(duì)應(yīng)(這步?jīng)]有也可以,因?yàn)闀?huì)在上一步的路徑下尋找)。
點(diǎn)擊解決方案名,選擇設(shè)置啟動(dòng)項(xiàng)目 -> 通用屬性 -> 啟動(dòng)項(xiàng)目 -> 單啟動(dòng)項(xiàng)目(選中UseDLL)
運(yùn)行UseDLL.cpp程序,成功執(zhí)行。
四. 小結(jié)
4.1 程序中的問題
-
error C2664: “LoadLibraryW”: 不能將參數(shù) 1 從“const char [10]”轉(zhuǎn)換為“LPCWSTR”與指向的類型無關(guān);轉(zhuǎn)換要求 reinterpret_cast、C 樣式轉(zhuǎn)換或函數(shù)樣式轉(zhuǎn)換
解決方法:選中項(xiàng)目,然后點(diǎn)擊屬性——>配置屬性——>常規(guī)——>項(xiàng)目默認(rèn)值——>字符集,選為“使用多字節(jié)字符集"
012304.png
-
fatal error LNK1104: 無法打開文件:×××.lib的解決辦法
一般情況是因?yàn)闆]有導(dǎo)入相應(yīng)的.lib文件,或者是導(dǎo)入的路徑有誤。給項(xiàng)目添加庫(kù)文件路徑。在VS中右擊項(xiàng)目點(diǎn)屬性:
配置屬性-->鏈接器-->常規(guī)-->附加目錄 。在里面填上庫(kù)文件所在的路徑即可。
fatal error LNK1104: 無法打開文件“x x x.def”
如果不想使用xxx.def文件,可以在項(xiàng)目-屬性-配置屬性-鏈接器-輸入 選項(xiàng)中,將右側(cè)的模塊定義文件刪掉,這樣就不會(huì)提示了。
4.2 vs的常用操作
-
添加頭文件:
配置屬性-->C/C++-->常規(guī)-->附加包含目錄 加上頭文件存放的目錄。
-
添加lib文件:
- 配置屬性-->鏈接器-->輸入-->附加依賴項(xiàng)加入庫(kù)名(×××.lib);或者是在cpp源文件中用#pragma comment(lib,"×××.lib")來代替。
- 將xxx.lib拷入工程所在目錄,或者執(zhí)行文件生成的目錄,或者系統(tǒng)Lib目錄中(如果lib文件是自己生成的,可以跳過這一步)。
- 給項(xiàng)目添加庫(kù)文件路徑:
在VS中右擊項(xiàng)目點(diǎn)屬性。配置屬性-->鏈接器-->常規(guī)-->附加目錄 。在里面填上庫(kù)文件所在的路徑即可。
4.3 windows小常識(shí)
在當(dāng)前目錄下運(yùn)行命令:shift鍵 + 鼠標(biāo)右鍵
-
首先將命令窗體屬性中的快速編輯模式選中打勾,這樣就可以一復(fù)制粘貼了。復(fù)制dos窗體中的內(nèi)容:右鍵->標(biāo)記->選擇復(fù)制內(nèi)容->回車鍵或者鼠標(biāo)右擊,粘貼的時(shí)候:鼠標(biāo)右鍵粘貼。
dos中不能使用快捷鍵。?



