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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM