原文地址:http://blog.csdn.net/dizuo/article/details/4103614
經常使用到第三方庫。比如:glut,freetype,sdl,大一點的有CEGUI,OSG等。
這些庫雖然是開源的,但是很多時候只是使用他們,一般不會去修改。
使用就會涉及到三個東西:.h .lib .dll文件。
主要有三種模式:
.h+.lib
.h+.lib+.dll
.dll
第二種是最常見的,其他兩種都比較少
一,下面自己實現一個dll和lib:
英文鏈接網址:http://msdn.microsoft.com/en-us/library/ms235636(VS.80).aspx
注意我的編譯器是vs2005。
1,建立一個win32控制檯應用程序, 輸入工程名字後點擊確定 點擊下一步, 選擇應用程序類型: DLL 附加選項選擇:空項目 。點擊確定
2,在空的工程中添加頭文件MathFuncsDll.h ,內容如下:
- // MathFuncsDll.h
- namespace MathFuncs
- {
- class MyMathFuncs
- {
- public:
- // Returns a + b
- static __declspec(dllexport) double Add(double a, double b);
- // Returns a - b
- static __declspec(dllexport) double Subtract(double a, double b);
- // Returns a * b
- static __declspec(dllexport) double Multiply(double a, double b);
- // Returns a / b
- // Throws DivideByZeroException if b is 0
- static __declspec(dllexport) double Divide(double a, double b);
- };
- }
3,添加MathFuncsDll.cpp文件內容如下:
- // MathFuncsDll.cpp
- // compile with: /EHsc /LD
- #include "MathFuncsDll.h"
- #include <stdexcept>
- using namespace std;
- namespace MathFuncs
- {
- double MyMathFuncs::Add(double a, double b)
- {
- return a + b;
- }
- double MyMathFuncs::Subtract(double a, double b)
- {
- return a - b;
- }
- double MyMathFuncs::Multiply(double a, double b)
- {
- return a * b;
- }
- double MyMathFuncs::Divide(double a, double b)
- {
- if (b == 0)
- {
- throw new invalid_argument("b cannot be zero!");
- }
- return a / b;
- }
- }
4, 設置工程屬性:
左邊解決方案資源管理器中右擊工程名:彈出的對話框中:左邊,配置屬性:/常規 下: 右邊 配置類型:選擇動態庫(.dll)
然後點擊生成:/ 生成 MathFuncDll。
此時可以到debug文件夾下去查看一下: MathFuncsDll.dll MathFuncsDll.lib 這兩個文件是我們最關心的。注意此時的MathFuncsDll.lib文件只有3kb大
二,使用dll和lib文件
新建一個空的win32控制檯應用程序:UseMathDll項目名字
添加cpp文件MyExecRefsDll.cpp:
- #include <iostream>
- #include "MathFuncsDll.h"
- #pragma comment(lib , "MathFuncsDll.lib") //導入lib文件
- using namespace std;
- int main()
- {
- double a = 7.4;
- int b = 99;
- cout << "a + b = " <<
- MathFuncs::MyMathFuncs::Add(a, b) << endl;
- cout << "a - b = " <<
- MathFuncs::MyMathFuncs::Subtract(a, b) << endl;
- cout << "a * b = " <<
- MathFuncs::MyMathFuncs::Multiply(a, b) << endl;
- cout << "a / b = " <<
- MathFuncs::MyMathFuncs::Divide(a, b) << endl;
- system("PAUSE");
- return 0;
- }
然後將MathFuncsDll.lib 和 MathFuncsDll.h文件 放到和MyExecRefsDll.cpp一個文件目錄下,也就是當前目錄下。將MathFuncsDll.dll可以放到system32下,當然了最簡單的做法就是放到UseMathDll這個工程的debug或者release文件夾裏面,將來生成的exe就可以直接在當前目錄下找到需要的dll文件。(動態鏈接文件,如果不把dll放入system32或者debug中,會導致程序無法運行。因爲真正的函數的可執行代碼都在dll中。lib文件僅僅只是一個索引,而.h文件僅僅只給出了一個藉口而已。)
此時就可以點擊運行了:
結果如下:
- a + b = 106.4
- a - b = -91.6
- a * b = 732.6
- a / b = 0.0747475
- 請按任意鍵繼續. . .
三,直接使用.h 和 lib文件
還是打開MathFuncsDll工程項目,依照條目一中的設置工程屬性:不過最後的配置類型 選擇靜態庫(.lib)
然後生成MathFuncsDll,然後到debug下面看一下,此時的MathFuncsDll.lib文件變成了29k
然後把MathFuncsDll.lib替換掉UseMathDll工程目錄下的原來的MathFuncsDll.ib文件,而且把Debug下的MathFuncsDll.dll刪掉,UseMathDll照樣正常運行。
下面是dll與lib的詳細對比:
.h頭文件是編譯時必須的,lib是鏈接時需要的,dll是運行時需要的。
附加依賴項的是.lib不是.dll,若生成了DLL,則肯定也生成 LIB文件。如果要完成源代碼的編譯和鏈接,有頭文件和lib就夠了。如果也使動態連接的程序運行起來,有dll就夠了。在開發和調試階段,當然最好都有。
.h .lib .dll三者的關係是:
H文件作用是:聲明函數接口
DLL文件作用是: 函數可執行代碼
當我們在自己的程序中引用了一個H文件裏的函數,編鏈器怎麼知道該調用哪個DLL文件呢?這就是LIB文件的作用: 告訴鏈接器 調用的函數在哪個DLL中,函數執行代碼在DLL中的什麼位置,這也就是爲什麼需要附加依賴項 .LIB文件,它起到橋樑的作用。如果生成靜態庫文件,則沒有DLL ,只有lib,這時函數可執行代碼部分也在lib文件中
目前以lib後綴的庫有兩種,一種爲靜態鏈接庫(Static Libary,以下簡稱“靜態庫”),另一種爲動態連接庫(DLL,以下簡稱“動態庫”)的導入庫(Import Libary,以下簡稱“導入庫”)。靜態庫是一個或者多個obj文件的打包,所以有人乾脆把從obj文件生成lib的過程稱爲Archive,即合併到一起。比如你鏈接一個靜態庫,如果其中有錯,它會準確的找到是哪個obj有錯,即靜態lib只是殼子。動態庫一般會有對應的導入庫,方便程序靜態載入動態鏈接庫,否則你可能就需要自己LoadLibary調入DLL文件,然後再手工GetProcAddress獲得對應函數了。有了導入庫,你只需要鏈接導入庫後按照頭文件函數接口的聲明調用函數就可以了。導入庫和靜態庫的區別很大,他們實質是不一樣的東西。靜態庫本身就包含了實際執行代碼、符號表等等,而對於導入庫而言,其實際的執行代碼位於動態庫中,導入庫只包含了地址符號表等,確保程序找到對應函數的一些基本地址信息。
一般的動態庫程序有lib文件和dll文件。lib文件是必須在編譯期就連接到應用程序中的,而dll文件是運行期纔會被調用的。如果有dll文件,那麼對應的lib文件一般是一些索引信息,具體的實現在dll文件中。如果只有lib文件,那麼這個lib文件是靜態編譯出來的,索引和實現都在其中。靜態編譯的lib文件有好處:給用戶安裝時就不需要再掛動態庫了。但也有缺點,就是導致應用程序比較大,而且失去了動態庫的靈活性,在版本升級時,同時要發佈新的應用程序纔行。在動態庫的情況下,有兩個文件,而一個是引入庫(.LIB)文件,一個是DLL文件,引入庫文件包含被DLL導出的函數的名稱和位置,DLL包含實際的函數和數據,應用程序使用LIB文件鏈接到所需要使用的DLL文件,庫中的函數和數據並不複製到可執行文件中,因此在應用程序的可執行文件中,存放的不是被調用的函數代碼,而是DLL中所要調用的函數的內存地址,這樣當一個或多個應用程序運行是再把程序代碼和被調用的函數代碼鏈接起來,從而節省了內存資源。從上面的說明可以看出,DLL和.LIB文件必須隨應用程序一起發行,否則應用程序將會產生錯誤。
-------------------------------------------------------------------------------------
靜態鏈接庫(Lib)與動態鏈接庫(DLL)的區別
靜態連接庫就是把(lib)文件中用到的函數代碼直接鏈接進目標程序,程序運行的時候不再需要其它的庫文件;動態鏈接就是把調用的函數所在文件模塊(DLL)和調用函數在文件中的位置等信息鏈接進目標程序,程序運行的時候再從DLL中尋找相應函數代碼,因此需要相應DLL文件的支持。
靜態鏈接庫與動態鏈接庫都是共享代碼的方式,如果採用靜態鏈接庫,則無論你願不願意,lib 中的指令都全部被直接包含在最終生成的 EXE 文件中了。但是若使用 DLL,該 DLL 不必被包含在最終 EXE 文件中,EXE 文件執行時可以“動態”地引用和卸載這個與 EXE 獨立的 DLL 文件。靜態鏈接庫和動態鏈接庫的另外一個區別在於靜態鏈接庫中不能再包含其他的動態鏈接庫或者靜態庫,而在動態鏈接庫中還可以再包含其他的動態或靜態鏈接庫。
“每一個lib文件就是若干函數(假設只有函數)的定義”
lib庫有兩種,一種是包含了函數所在DLL文件和文件中函數位置的信息,稱爲導出庫;一種是包含函數代碼本身,一般現有的DLL,用的是前一種庫;以前在DOS下的TC/BC等,是後一種庫。包含函數原型聲明的,是頭文件(.h)。
“通過#include包含這些函數聲明的頭文件後,我們的應用程序就可以使用lib文件中的函數”
還要指定編譯器鏈接相應的庫文件。在IDE環境下,一般是一次指定所有用到的庫文件,編譯器自己尋找每個模塊需要的庫;在命令行編譯環境下,需要指定每個模塊調用的庫。
“那他和直接給出那個函數定義的文件,比如.cpp文件,和頭文件有什麼區別,靜態鏈接庫有什麼用”
cpp文件是源代碼,庫文件是編譯後的二進制代碼,比如你可以調用Windows的API,但是不能看到其源代碼一樣。
“還有不明白的是,靜態鏈接庫中的lib文件只要用到,則整個lib文件的內容都放進了exe文件中,那它是被編譯進去還是鏈接的時候連接進去的呢?”
是在鏈接的時候將lib鏈接到目標代碼中。
靜態鏈接庫(Lib)
在VC++6.0中new一個名稱爲libTest的static library工程,
並新建lib.h和lib.cpp兩個文件,lib.h和lib.cpp的源代碼如下:
//文件:lib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y); //聲明爲C編譯、連接方式的外部函數
#endif
//文件:lib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}
編譯這個工程就得到了一個.lib文件,這個文件就是一個函數庫,它提供了add的功能。將頭文件和.lib文件提交給用戶後,用戶就可以直接使用其中的add函數了。
標準Turbo C2.0中的C庫函數(我們用來的scanf、printf、memcpy、strcpy等)就來自這種靜態庫。
下面來看看怎麼使用這個庫,在libTest工程所在的工作區內new一個libCall工程。libCall工程僅包含一個main.cpp文件,它演示了靜態鏈接庫的調用方法,其源代碼如下:
#include <stdio.h>
#include "..\lib.h"//不可丟失
#pragma comment( lib, "..\\debug\\libTest.lib" ) //指定與靜態庫一起連接
int main(int argc, char* argv[])
{
printf( "2 + 3 = %d", add( 2, 3 ) );
}
靜態鏈接庫的調用就是這麼簡單,或許我們每天都在用,可是我們沒有明白這個概念。代碼中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文件生成的.obj文件應與libTest.lib一起連接