1. 程式人生 > >C++_編寫動態連結庫

C++_編寫動態連結庫

動態連結庫簡介

動態連結庫(Dynamic Link Library 或者 Dynamic-link Library,縮寫為 DLL),是微軟公司在微軟Windows作業系統中,實現共享函式庫概念的一種方式。這些庫函式的副檔名是 ”.dll”、”.ocx”(包含ActiveX控制的庫)或者 “.drv”(舊式的系統驅動程式)。 
動態連結提供了一種方法,使程序可以呼叫不屬於其可執行程式碼的函式。函式的可執行程式碼位於一個 DLL 檔案中,該 DLL 包含一個或多個已被編譯、連結並與使用它們的程序分開儲存的函式。DLL 還有助於共享資料和資源。多個應用程式可同時訪問記憶體中單個 DLL 副本的內容。 
通過使用DLL可以實現模組化,使之由相對獨立的元件組成,可以更快的載入應用各個模組的功能,還可以更容易的將更新應用於各個模組。 
例如,一個程式含有很多個功能,將他們都編寫成DLL檔案,當軟體更新時,只需更新相應的DLL檔案而不需要重新安裝整個程式

編寫一個DLL

1、新建工程,使用VS2015編寫一個實現兩數相加的DLL 
檔案->新建專案->win32專案->下一步->DLL->勾選預編譯標頭檔案,匯出符號->完成

此時VS2015會自動生成3個頭檔案和3個cpp檔案 
這裡寫圖片描述

其中TestDll.h是DLL的標頭檔案

#ifdef TESTDLL_EXPORTS
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif

// 此類是從 TestDll.dll 匯出的
class TESTDLL_API CTestDll {
public:
    CTestDll(void);
    // TODO:  在此新增您的方法。
};

//匯出的變數
extern TESTDLL_API int nTestDll;

//匯出函式
TESTDLL_API int fnTestDll(void);

stdafx.h包含需要的標頭檔案

#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN             
#include <windows.h>

TestDll.cpp是原始檔 
DllMain是動態連結庫的入口點,在DllMain函式中,hModule引數是該DLL模組的控制代碼,代表這個檔案的映象載入到程序的地址空間使用的基地址,ul_reason_for_call引數的指標是本次呼叫的原因,包括

  • DLL_PROCESS_ATTACH,表示動態連結庫剛被某一個程序載入,對映到了某一個地址空間,可以在此進行初始化
  • DLL_THREAD_ATTACH,動態連結庫將被解除安裝,可以在這進行資源釋放
  • DLL_THREAD_DETACH,應用程式建立了一個新的程序
  • DLL_PROCESS_DETACH,某個程序正常終止
  • // dllmain.cpp : 定義 DLL 應用程式的入口點。
    #include "stdafx.h"
    
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }


2、將TestDll.h中的匯出函式改為

TESTDLL_API int fnTestDll(int a,int b);

刪除匯出變數和匯出類部分

3、將TestDll.cpp檔案中匯出函式改為

TESTDLL_API int fnTestDll(int a, int b)
{
    return a+b;
}

刪除匯出類和匯出變數部分 
注:Dll能夠定義兩種函式,一種是內部函式,一種是匯出函式。內部函式只能被定義這個函式的模組使用,而匯出函式不僅可以在本模組呼叫,還可以被其他模組呼叫。dll的主要功能就是向外匯出函式供其他模組使用

此時一個簡單的兩數相加的DLL就寫完了,接下來便是如何載入這個Dll了,載入這個dll有兩種方法,一種是隱式動態連結,一種是顯示動態連結

隱式動態連結

新建一個win32控制檯程式,生成剛剛寫的dll,將TestDll.lib,TestDll.dll,TestDll.h複製到該控制檯程式的資料夾下,然後新增下面的程式碼

#include "stdafx.h"
#include <iostream>
#include "TestDll.h"

using namespace std;
#pragma comment(lib,"TestDll")

int main()
{
    int a, b, result;
    while (cin >> a >> b)
    {
        result = fnTestDll(a, b);
        cout << "Result : " << result << endl;
    }
    return 0;
}

#pragma comment(lib,”TestDll”)語句表示要連線到TestDll.lib庫,使用隱式動態連結需要指明庫所在的位置(本例中在當前資料夾下),靜態變異也會使可執行檔案的體積變大 
測試結果 
這裡寫圖片描述 
可以看到執行成功

顯式動態連結

模組使用LoadLIbrary()或者LoadLIbraryEx()函式顯式載入DLL,載入後呼叫GetProcAddress()函式取得DLL匯出函式的地址,然後呼叫函式。

1、在原DLL工程中建立一個模組定義檔案(DEF)來指定要匯入的函式。 
在TestDll中專案->新增新項->模組定義檔案(.def) 

然後在其中新增

EXPORTS
fnTestDll

fnTestDll表示要向外匯出的函式名 
重新生成該專案 
2、新建一個win32控制檯程式,新增如下程式碼

  • // ExplicitTestDll.cpp : 定義控制檯應用程式的入口點。
    //
    
    #include "stdafx.h"
    #include <iostream>
    #include <Windows.h>
    using namespace std;
    typedef int(*PFNEXPORTFUNC)(int, int);
    int main()
    {
        int a, b, result;
        while (cin >> a >> b)
        {
            HMODULE hModule = LoadLibrary(_T("TestDll.dll"));
            if (hModule != NULL)
            {
                PFNEXPORTFUNC mDLLFuncAdd = (PFNEXPORTFUNC)GetProcAddress(hModule, "fnTestDll");
                if (mDLLFuncAdd != NULL)
                {
                    result = mDLLFuncAdd(a, b);
                    cout << "Result: " << result << endl;
                }
                FreeLibrary(hModule);
            }
        }
    
        return 0;
    }


顯式呼叫DLL分為三個步驟

  1. 宣告要匯出的DLL函式
  2. 載入目標DLL,即 LoadLibrary()函式,將DLL載入到程序的虛擬地址空間,若成功則返回該DLL模組的控制代碼,否則返回NULL
  3. 獲得匯出函式的地址,即GetProcAddress()函式,成功時返回函式地址,否則返回NULL

測試結果 

這裡寫圖片描述

原文地址:https://blog.csdn.net/a7055117a/article/details/47733247

參考:

  1. 百度百科
  2. 《小小黑客之路-黑客工具、攻防及防火牆入門》