1. 程式人生 > 其它 >Windows的靜態庫與動態庫

Windows的靜態庫與動態庫

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動態庫的使用

  • 隱式連結(作業系統負責使動態庫執行)

    1. 標頭檔案和函式原型

      ​ 可以在函式原型的宣告前,增加_declspec(dllimport)

    2. 匯入動態庫的LIB檔案

    3. 在程式中使用函式

    4. 隱式連結的情況,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;
    }
    
  • 顯示連結(程式設計師自己負責使動態庫執行)

    1. 定義函式指標型別 typedef

    2. 載入動態庫

      HMODULE LoadLibrary(
      	LPCTSTR lpFileName //動態庫檔名或全路勁
      ); //返回DLL的例項控制代碼(HINSTANCE)
      
    3. 獲取函式地址

      FARPROC GetProcAddress(
      	HMODULE hModule  //DLL控制代碼
          LPCSTR lpProcName //函式名稱
      ); //成功返回函式地址
      
    4. 使用函式

    5. 解除安裝動態庫

      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;
    }