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