C++ dll動態程式庫建置架構筆記(CMake build) -2 如何使用dll程式

這篇介紹如何讀取及使用 C++ 動態程式庫。

我的系統環境:
  • Win10
  • CMake 3.18.4
  • VisualStudio 2019

一、介紹做法

我所知道的有兩種做法:

1.需要header、lib檔

    優點:程式碼較少、不用重寫定義
    缺點:還不知道

2.只需要dll檔

    優點:可在程式執行中期匯入及釋放
    缺點:程式碼較繁瑣、使用的人要知道 dll 裡面的 function 名稱及參數架構

二、說明

作法一、需要header、lib檔

需要 include dll 程式提供給外部使用的 function 定義檔,也就是有加 __declspec(dllexport) 的部分。
以下是使用上一篇文章所做出來的 dll 程式。

test.cpp
#include <stdio.h>
#include "my_math.h"

int main()
{
    int a = 10;
    int b = 20;

    int res = Add(a,b);
    printf("Add %d\n", res);

    res = Subtraction(a,b);
    printf("Subtraction %d\n", res);

    res = Multiply(a,b);
    printf("Multiply %d\n", res);

    math_class math_cls;
    res = math_cls.Add(a,b);
    printf("math_class Add %d\n", res);

    res = math_cls.Subtraction(a,b);
    printf("math_class Subtraction %d\n", res);
    
    res = math_cls.Multiply(a,b);
    printf("math_class Multiply %d\n", res);

    return 0;
}

CMakeLists.txt
set (THE_MODULE "Dll_1Layer_main")
set (Lib_project "Dll_1Layer_math") # connect Dll_1Layer_math.lib

# include header
INCLUDE_DIRECTORIES(../math/include)

# output Dll_1Layer_main.exe
ADD_EXECUTABLE(${THE_MODULE} test.cpp)
TARGET_LINK_LIBRARIES(${THE_MODULE} ${Lib_project})
TARGET_LINK_LIBRARIES(${THE_MODULE} ${Lib_project})
這行的意思是告訴 CMake 連接 dll 專案"Dll_1Layer_math"。

作法二、只需要dll檔

這個做法使用 LoadLibraryA() 匯入 dll 檔,要先定義好 dll 裡面 function 架構,大小寫都不能錯,再讀入這個 function 的 memory address,之後才能使用這個 function。
這個做法使用完要釋放掉,有個優點,能在程式執行中釋放掉 dll 程式,不暫記憶體,但是寫起來很痛苦,程式碼很多。

舉例如下:
function 架構: typedef int(*Add)(int a, int b); 
讀入 memory address:add = (Add)GetProcAddress(hLib, "Add");
釋放掉 dll 程式:FreeLibrary(hLib);

test_withoutLib.cpp
#include <stdio.h>
#include <windows.h>

struct My_math_dll
{
private:
    HMODULE hLib;
    bool enable = false;

public:
    //定義每個 function 原始的架構
    typedef int(*Add)(int a, int b);
    typedef int(*Subtraction)(int a, int b);
    typedef int(*Multiply)(int a, int b);
    typedef int(*Divided)(int a, int b);
    typedef int(*Squared)(int a, int b);

    //宣告 function 在這邊使用時的名稱
    Add add;
    Subtraction subtraction;
    Multiply multiply;
    Divided divided;
    Squared squared;

    ~My_math_dll()
    {
        release();
    }

    int loadAPI()
    {
        // 讀入 dll
        hLib = LoadLibraryA("Dll_1Layer_math.dll");
        
        // If the dll file fails, the memory address will be 0.
        printf_s("Dll_1Layer_math.dll address:%p\n", hLib);
        if (!hLib)
        { 
            printf_s("load Dll_1Layer_math.dll fail!\n");
            return 0;
        }
        // 將每個 function 的 address 讀入
        add = (Add)GetProcAddress(hLib, "Add");
        subtraction = (Subtraction)GetProcAddress(hLib, "Subtraction");
        multiply = (Multiply)GetProcAddress(hLib, "Multiply");
        divided = (Divided)GetProcAddress(hLib, "Divided");
        squared = (Squared)GetProcAddress(hLib, "Squared");

        // Check the memory address obtained by the function API.
        // If the function API fails, the memory address will be 0.
        printf_s("API address: %p %p\n", add, subtraction);
        enable = true;
        return 0;
    }

    void release()
    {
        if (enable)
        {
            FreeLibrary(hLib);
            enable = false;
        }
    }
};

int main()
{
    My_math_dll mydll;
    mydll.loadAPI();

    int a = 10;
    int b = 20;

    int res = mydll.add(a,b);
    printf("Add %d\n", res);

    res = mydll.subtraction(a,b);
    printf("Subtraction %d\n", res);

    res = mydll.multiply(a,b);
    printf("Multiply %d\n", res);

    return 0;
}

CMakeLists.txt
set (THE_MODULE "Dll_1Layer_main_withoutLib")

# output Dll_1Layer_main_withoutLib.exe
ADD_EXECUTABLE(${THE_MODULE} test_withoutLib.cpp)
這就不用連接dll,因為要等程式執行到 LoadLibraryA() 才會匯入dll檔


留言

這個網誌中的熱門文章

C# 模擬鍵盤滑鼠控制電腦

python pyautogui 簡介

android 定時通知(永久長期的) 本篇只講AlarmManager使用

raspberrypi 開機自動執行程式 與 在terminal開啟第二個terminal執行python

python nn 聲音辨識 -1 傅立葉轉換