Windows的靜態庫與動態庫
1.靜態庫
1.1 靜態庫特點
- 執行不存在
- 靜態庫原始碼被連結到呼叫程式中
- 目標程式的歸檔
1.2 C語言靜態庫
- C靜態庫的建立
- 建立一個靜態庫專案。
- 新增庫程式,原始檔使用C檔案。
int Clib_add(int add1,int add2){
return add1+add2;
}
int Clib_sub(int sub1,int sub2){
return sub1 - sub2;
}
- C靜態庫的使用
- 庫路勁設定:可以使用pragma關鍵字設定
pragma comment(lib,"../lib/clib.lib")
#pragma comment(lib,"../Debug/Clib.lib")
#include <stdio.h>
#include <process.h>
int main(){
int sum,sub;
sum = Clib_add(5,3);
sub = Clib_sub(5,3);
printf("sum=%d,sub=%d\n",sum,sub);
system("pause");
return 0;
}
1.3 C++語言靜態庫
- C++靜態庫的建立
- 建立一個靜態庫專案
- 新增庫程式,原始檔使用cpp檔案
int CPPlib_add(int add1,int add2){
return add1 + add2;
}
int CPPlib_sub(int sub1,int sub2){
return sub1 - sub2;
}
- C++靜態庫的使用
- 庫路徑設定:可以使用pragma關鍵字設定
pragma comment(lib,"../lib/cpplib.lib")
#include <iostream>
using namespace std;
//給連結器看
int CPPlib_add(int add1,int add2);
int CPPlib_sub(int sub1,int sub2);
//給編譯器看
#pragma comment(lib,"../Debug/CPPlib.lib") //?CPPlib_add@@YAHHH@Z / ?CPPlib_sub@@YAHHH@Z
extern "C" int Clib_add(int add1,int add2);
extern "C" int Clib_sub(int sub1,int sub2);
#pragma comment(lib,"../Debug/Clib.lib") //Clib_add / Clib_sub
int main(){
//調c++庫函式
int sum = CPPlib_add(5,4); //?CPPlib_add@@YAHHH@Z
int sub = CPPlib_sub(5,4); //?CPPlib_sub@@YAHHH@Z
cout << "sum=" << sum << ",sub=" << sub << endl;
//調c庫函式
sum = Clib_add(5,3); //?Clib_add@@YAHHH@Z 使用extern "C",編譯時不換函式名-->Clib_add
sub = Clib_sub(5,3); //?Clib_sub@@YAHHH@Z -->Clib_sub
cout << "sum=" << sum << ",sub=" << sub << endl;
system("pause");
return 0;
}
2.動態庫
2.1 動態庫特點
動態庫的特點
- 執行時獨立存在
- 原始碼不會連結到執行程式
- 使用時載入(使用動態庫必須使動態庫執行)
與靜態庫的比較
由於靜態庫是將程式碼嵌入到使用程式中,多個程式使用時,會有多分程式碼,所以程式碼體積會增大。動態庫的程式碼只需要存在一份,其他程式通過函式地址使用,所以程式碼體積小。
靜態庫發生變化後,新的程式碼需要重新連結嵌入到執行程式中。動態庫發生變化後,如果庫中函式的定義(或地址)未變化,其他使用DLL的程式不需要重新連結。
2.2動態庫建立
建立動態庫專案
新增庫程式
庫程式匯出 - 提供給使用者庫中的函式等資訊。
- 宣告匯出:使用_declspec(dllexport)匯出函式
注意:動態庫編譯連結後,也會有LIB檔案,是作為動態庫函式對映使用,與靜態庫不完全相同。
製作動態庫
_declspec(dllexport) int CPPdll_add(int add1,int add2){
return add1 + add2;
} _declspec(dllexport) int CPPdll_sub(int sub1,int sub2){
return sub1 - sub2;
} _declspec(dllexport) int CPPdll_mul(int mul1,int mul2){
return mul1 * mul2;
}
動態庫原理圖
- 模組定義檔案:.def
例如:LIBRARY DLLFunc //庫
EXPORTS //庫匯出表
DLL_Mul @1 //匯出的函式
不加宣告匯出
int CPPdll_add(int add1,int add2){
return add1 + add2;
} int CPPdll_sub(int sub1,int sub2){
return sub1 - sub2;
} int CPPdll_mul(int mul1,int mul2){
return mul1 * mul2;
}
新增模組定義匯出檔案
LIBRARY CPPDll
EXPORTS
CPPdll_add @1
CPPdll_sub @2
CPPdll_mul @3
2.3動態庫的使用
隱式連結(作業系統負責使動態庫執行)
標頭檔案和函式原型
可以在函式原型的宣告前,增加_declspec(dllimport)
匯入動態庫的LIB檔案
在程式中使用函式
隱式連結的情況,dll檔案可以存放的路徑:
- 與執行檔案中同一個目錄下(建議放在此目錄下)
- 當前工作目錄
- Windows目錄
- Windows/System32目錄
- Windows/System
- 環境變數PATH指定目錄
#include <iostream>
using namespace std; _declspec(dllimport)int CPPdll_add(int add1,int add2);
_declspec(dllimport)int CPPdll_sub(int sub1,int sub2);
_declspec(dllimport)int CPPdll_mul(int mul1,int mul2);
//通知連結器到哪抓編號和dll檔名(CPPDll.dll)
#pragma comment(lib,"../Debug/CPPDll.lib") int main(){
int sum = CPPdll_add(5,4);
int sub = CPPdll_sub(5,4);
int mul = CPPdll_mul(5,4);
cout << "sum=" << sum << ",sub=" << sub << ",mul=" << mul << endl;
system("pause");
return 0;
}
顯示連結(程式設計師自己負責使動態庫執行)
定義函式指標型別 typedef
載入動態庫
HMODULE LoadLibrary(
LPCTSTR lpFileName //動態庫檔名或全路勁
); //返回DLL的例項控制代碼(HINSTANCE)
獲取函式地址
FARPROC GetProcAddress(
HMODULE hModule //DLL控制代碼
LPCSTR lpProcName //函式名稱
); //成功返回函式地址
使用函式
解除安裝動態庫
BOOL FreeLibrary(
HMODULE hModule //DLL的例項控制代碼
);
#include <iostream>
#include <Windows.h>
using namespace std; typedef int(*FUNC)(int m,int n);
int main(){
HINSTANCE hDll = LoadLibrary("CPPDll.dll");
cout << "hDll:" << hDll << endl; FUNC add = (FUNC)GetProcAddress(hDll,"CPPdll_add");
cout << "addAdress:" << add << endl;
int sum = add(5,4);
cout << "sum:" << sum << endl; FUNC sub = (FUNC)GetProcAddress(hDll,"CPPdll_sub");
cout << "subAdress:" << sub << endl;
int su = sub(5,4);
cout << "sub:" << su << endl; FUNC mul = (FUNC)GetProcAddress(hDll,"CPPdll_mul");
cout << "mulAdress:" << mul << endl;
int mu = mul(5,4);
cout << "mul:" << mu << endl; system("pause");
FreeLibrary(hDll);
return 0;
}
2.4動態庫中封裝類
在類名稱前增加_declspec(dllexport)定義,例如:
class _declspec(dllexport) CMath{
...
};
通常使用預編譯開關切換類的匯入匯出定義,例如:
#ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport) //dll
#else
#definE EXT_CLASS _declspec(dllimport) //使用者
#endif class EXT_CLASS CMath{
...
};
例子:
標頭檔案
#ifndef _ClASSDLL_H
#define _CLASSDLL_H #ifdef DLLCLASS_EXPORTS
#define EXT_CLASS _declspec(dllexport) //dll
#else
#define EXT_CLASS _declspec(dllimport) //使用者
#endif class EXT_CLASS CMath{
public:
int add(int m,int n);
int sub(int m, int n);
}; #endif
原始檔
#define DLLCLASS_EXPORTS
#include "ClassDll.h" int CMath::add(int m,int n){
return m + n;
} int CMath::sub(int m,int n){
return m - n;
}
呼叫動態庫中的封裝類
#include "..\ClassDll\ClassDll.h"
#include <iostream>
using namespace std;
#pragma comment(lib,"../Debug/ClassDll.lib") int main(){
CMath myMath;
int sum = myMath.add(5,4);
int sub = myMath.sub(5,4);
cout << "sum=" << sum << ",sub=" << sub << endl;
system("pause");
return 0;
}