學習內容:本博客介紹了Windows下使用Visual C++ 6.0製做與使用靜態庫與動態庫的方法。html
庫(Library)是能夠複用的代碼,在一些大型項目中經常會用到庫。linux
本質上說,庫是一種可執行代碼的二進制形式,能夠被操做系統載入內存執行。程序員
在前期的課程中,咱們學習了Linux下靜態庫與動態庫的使用,Windows下與之對應:windows
所謂靜態、動態是指連接。回顧一下將一個程序編譯成可執行文件的步驟:app
靜態庫與動態庫的區別就在於,【連接階段】如何處理庫,從而鏈接成可執行程序。函數
之因此成爲【靜態庫】,是由於在連接階段,會將彙編生成的目標文件.o與引用到的庫一塊兒連接打包到可執行文件中。所以對應的連接方式稱爲靜態連接。學習
試想一下,靜態庫與彙編生成的目標文件一塊兒連接爲可執行文件,那麼靜態庫一定跟.o文件格式類似。其實一個靜態庫能夠簡單當作是一組目標文件(.o/.obj文件)的集合,即不少目標文件通過壓縮打包後造成的一個文件。測試
靜態庫的特色可總結爲:ui
Linux下使用ar工具、Windows下使用lib.exe,將目標文件壓縮到一塊兒,而且對其進行編號和索引,以便於查找和檢索。通常建立靜態庫的步驟如圖所示:
經過上面的介紹發現靜態庫,容易使用和理解,也達到了代碼複用的目的,那爲何還須要動態庫呢?
爲何須要動態庫,其實也是靜態庫的特色致使。
動態庫在程序編譯時並不會被鏈接到目標代碼中,而是在程序運行是才被載入。不一樣的應用程序若是調用相同的庫,那麼在內存裏只須要有一份該共享庫的實例,規避了空間浪費問題。動態庫在程序運行是才被載入,也解決了靜態庫對程序的更新、部署和發佈頁會帶來麻煩。用戶只須要更新動態庫便可,增量更新。
動態庫特色總結以下:
綜上所述,靜態庫與動態庫的不一樣點在於:代碼被載入的時刻不一樣。
1.咱們先來完成準備工做。打開Visual C++ 6.0,點擊左上角/File->New...
,選擇須要新建的對象:
選擇Workspaces
選項卡,輸入Workspaces name
以及Location
,新建一個工做區:
本篇博客所用到的Project都建在這個工做區就能夠啦~
2.選中咱們新建的workspace,右擊選擇Add New Project to Workspace...
咱們先嚐試建立靜態庫,所以在Project選項卡選擇Win32 Static Library
,並在右側輸入Project name
便可完成項目建立。
接下來一路next,一個Win32 Static Library
格式的Project就建成了:
3.注意到一個工程下面有兩個文件夾,分別是Source Files
和Header Files
。顧名思義,Source Files
用來存放源文件,如.cpp
等;Header Files
則用來存放頭文件。
咱們先來建一個頭文件,存放函數說明。
選中Header Files
文件夾,點擊File->New...->Files->C/C++ Header File
,並輸入文件名,完成建立。
內容爲:
int statlib_demo(int a, int b);
4.一樣的方法,在Source Files
中新建staticlib.cpp
,此次須要選中C++ Source File
。
內容爲:
#include "staticlib.h" int statlib_demo(int a, int b) { if (a > b) return a-b; else return a+b; }
就此完成了代碼基本的編寫。
5.選擇這個Project,依次點擊Compile
和Build
按鈕:
打開Project所在文件夾中的Debug
文件夾,能夠看到成功生成lib.lib
,這就是咱們須要的靜態庫啦~
接下來咱們編寫代碼,對生成的靜態庫進行測試。
1.與前面相似,此次在workspace中新建一個Win32 Console Application
的Project:
選擇A "Hello, world!" application.
:
能夠看到在工做區生成了一個名爲libTest
的Project,其中包含一些自動生成的文件和代碼:
2.將剛纔生成的lib.lib
拷貝到新建的這個項目文件夾下,以進行後續的調用。
3.修改這個Project中的代碼,完成對靜態庫調用的測試:
libTest.cpp
:
#include "stdafx.h" #include "../lib/staticlib.h" //要注意在這裏添加頭文件的位置 int main(int argc, char* argv[]) { printf("Hello World!\n"); int iRet; iRet = statlib_demo(3,5); printf("the result of value is %x\n",iRet); return 0; }
StdAfx.h
:只須要在#include <stdio.h>
下一行加上#pragma comment (lib,"lib")
便可。其中,第一個「lib」表明連接一個lib庫,第二個「lib」是這個庫的名字,需根據實際狀況修改。
StdAfx.cpp
不須要作改動。
4.完成了以上代碼的編寫和修改,Build
這個項目,並Execute Program
就能夠了。運行結果以下:
到此爲止,咱們完成了靜態庫的編寫和測試。最關鍵的點就是#pragma comment(lib,"XXX")
這句預處理指令了。
靜態連接時,編譯器將函數和過程都編譯到exe文件,且函數的相對位置在連接時已經肯定。多個程序調用同一個函數時,內存中保存多份函數。
DLL(Dynamic Linkable Library),動態連接庫,能夠向程序提供一些函數、變量或類。這些能夠直接拿來使用。
DLL中通常定義有兩種類型的函數:導出函數和內部函數。其中:
爲了使生成的dll文件能夠被外部程序調用,咱們主要關注導出函數的生成與使用。
從dll中聲明導出函數有兩種方式:
下面咱們來詳細瞭解這兩種方式的用法。
1.一樣地,在workspace新建一個Project,選擇爲Win32 Dynamic-Link Library
:
選擇A simple DLL project.
:
能夠看到在工做區生成了一個名爲dll_def
的Project,其中包含一些自動生成的文件和代碼:
2.修改這個Project中的代碼,完成動態庫的編寫:
dll_def.cpp
:
#include "stdafx.h" #include "dll_def.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } int cmp3_demo(int a, int b) { if (a > b) return a; else return b; }
在Header Files
中新建一個dll_def.h
,內容爲:
int cmp3_demo(int a, int b);
其餘已存在的文件不須要作修改。
3.最關鍵的步驟到了!!!——編寫.def
文件。
.def文件的規則爲:
針對咱們這個Project,須要在Source Files
中新建一個dll_def.def
文件,內容爲:
LIBRARY DLL_DEF EXPORTS cmp3_demo
4.選擇這個Project,依次點擊Compile
和Build
按鈕,打開Project所在文件夾中的Debug
文件夾,能夠看到成功生成dll_def.dll
,由此完成了動態庫的生成。
下面咱們嘗試使用第二種方法生成dll文件。
1.在workspace新建一個Project,選擇爲Win32 Dynamic-Link Library
,具體過程再也不贅述。
2.修改這個Project中的代碼,完成動態庫的編寫:
dll_decl.cpp
:
#include "stdafx.h" #include "dll_decl.h" BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } LIB_API int cmp_demo(int a, int b) { if (a < b) return a; else return b; }
其他已存在的文件中的代碼不須要作修改。
3.這個方法最關鍵的一步!!!——編寫dll_decl.h
頭文件。
導出函數的聲明時須要有_declspec(dllexport)關鍵字。具體形式爲:
_declspec(dllexport) <返回類型> <導出函數名> (<函數參數>);
在本Project中,編寫的代碼以下:
#define LIB_API __declspec(dllexport) LIB_API int cmp_demo(int a, int b);
4.與上文一樣的步驟,選擇這個Project,依次點擊Compile
和Build
按鈕,打開Project所在文件夾中的Debug
文件夾,能夠看到成功生成dll_decl.dll
,由此使用第二種方法,完成了動態庫的生成:
1.在workspace中新建一個Win32 Console Application
的Project,名爲dllstaticTest
。首先,須要把以前生成的dll文件和對應的lib文件拷到這個文件夾下,以完成後續調用。
2.隱式連接動態庫主要有三種方法:
①將lib文件直接加入新建的這個測試project中。
②在測試程序所在project的StdAfx.h文件中增長#pragma comment (lib,"XXX")
實現。
③在本Project下的「project」菜單的「setting」下對話框中選擇這個工程,並在對應的「Link」選項窗口中的「Object/library modules」下增長鬚要導入的lib文件:
3.修改這個Project中的代碼,完成動態庫的測試:
dllstaticTest.cpp
:
#include "stdafx.h" #include "../dll_decl/dll_decl.h" int main(int argc, char* argv[]) { int iRet; printf("Hello World!\n"); iRet = cmp_demo(3,5); printf("return value is: %x\n",iRet); return 0; }
上面介紹的動態庫使用方法和靜態庫相似,屬於隱式連接,編譯的時候指定相應的庫和查找路徑。其實,動態庫還經過調用API,如:LoadLibrary、GetProcAddress、FreeLibrary完成動態調用(即顯式連接)。
應用程序必須進行函數調用以在運行時顯式加載 DLL。爲顯式連接到 DLL,應用程序需經過調用函數完成如下步驟:
代碼以下所示:
#include "stdafx.h" #include <windows.h> #include "../dll_def/dll_def.h" int main(int argc, char* argv[]) { printf("Hello World!\n"); //LoadLibrary函數裝載dll HMODULE hmod = LoadLibrary("dll_def.dll"); if(!hmod) { printf("load library failed\n"); return 0; } typedef int (*LoadProc)(int x, int y); //GetProcAddress得到"cmp3_demo"函數地址 LoadProc Load_proc = (LoadProc)GetProcAddress(hmod,"cmp3_demo"); if(!Load_proc) { printf("load function cmp3_demo failed\n"); return 0; } //經過函數指針間接調用"cmp3_demo" int iRet = Load_proc(3,5); printf("the cmp3_demo the value is:%x\n",iRet); //釋放dll FreeLibrary(hmod); return 0; }