首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 動態連結庫 (DLL) 開發與呼叫完整實戰:建立、匯出、隱式/顯式連結三種模式全解析

     編輯:關於VC++

前言:DLL 核心價值

DLL (Dynamic Link Library) 實現程式碼共享、模組化、獨立升級。多進程共享記憶體映像,減少體積;介面不變時,僅替換 DLL 即可升級功能。VC++ 支援三類 DLL:Non-MFC、Regular (靜態/動態連結 MFC)、Extension (MFC 擴充)。本文以 Non-MFC DLL 為例,示範完整建立與呼叫流程。

一、DLL 類型對比

類型特點呼叫者限制適用場景
Non-MFC DLL純 C/C++,無 MFC 依賴任何語言 (C, C++, VB, C#, Delphi)通用演算法庫、硬體介面、跨平台核心
Regular DLL (動態連結 MFC)使用 MFC 類別,有 CWinApp 派生類支援 C++ 類別/函式匯出,非 MFC 應用也可呼叫 C 介面MFC 控件庫、對話方塊封裝、業務邏輯模組
Extension DLL僅匯出 MFC 派生類 (如 CToolBar 派生)僅限 MFC 應用程式MFC 類別庫擴充、自訂控件

二、Non-MFC DLL 建立步驟 (Win32 Dynamic-Link Library 專案)

1. 專案建立

File → New → Project → Win32 Dynamic-Link Library,命名 `MyDll`。

2. 入口函式 DllMain (可選)

BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID reserved) {
    switch (reason) {
        case DLL_PROCESS_ATTACH: /* 進程載入:初始化資源 */ break;
        case DLL_THREAD_ATTACH:  /* 執行緒建立 */ break;
        case DLL_THREAD_DETACH:  /* 執行緒結束 */ break;
        case DLL_PROCESS_DETACH: /* 進程卸載:釋放資源 */ break;
    }
    return TRUE;
}

3. 匯出函式兩種方式

方式 A:`__declspec(dllexport)` (推薦,無需 .def)

// MyDll.h
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

extern "C" MYDLL_API int Add(int a, int b);
extern "C" MYDLL_API int Sub(int a, int b);

// MyDll.cpp
#include "MyDll.h"
extern "C" MYDLL_API int Add(int a, int b) { return a + b; }
extern "C" MYDLL_API int Sub(int a, int b) { return a - b; }

方式 B:模組定義檔 .def

; MyDll.def
LIBRARY "MyDll"
EXPORTS
    Add
    Sub
    ; Add @1  ; 可指定序數,供 GetProcAddress 使用

專案設定 → Link → Module Definition File 填入 `MyDll.def`。此時標頭檔不需 `__declspec`。

4. 編譯產出

  • `MyDll.dll`:執行期載入的動態庫
  • `MyDll.lib`:匯入庫 (含符號表),隱式連結時需連結此庫

三、應用程式呼叫 DLL 兩種模式

模式一:隱式連結 (Load-time Dynamic Linking)

特點:啟動時自動載入 DLL,程式碼簡潔,但 DLL 缺失會導致啟動失敗。

// App.h / stdafx.h
#pragma comment(lib, "MyDll.lib") // 告訴連結器連結匯入庫
extern "C" int Add(int a, int b);
extern "C" int Sub(int a, int b);

// App.cpp
#include "App.h"
int main() {
    int result = Add(10, 20); // 直接呼叫
    printf("Result: %d\n", result);
    return 0;
}

部署:`MyDll.dll` 放應用程式目錄或 `System32`/`PATH` 目錄。

模式二:顯式連結 (Run-time Dynamic Linking)

特點:執行期動態載入/卸載,彈性高、可選擇性載入、無需 `.lib`。

#include 
#include 

typedef int (*PFN_ADD)(int, int);
typedef int (*PFN_SUB)(int, int);

int main() {
    HINSTANCE hDll = LoadLibrary(_T("MyDll.dll"));
    if (!hDll) { printf("Load DLL failed: %d\n", GetLastError()); return 1; }

    PFN_ADD pAdd = (PFN_ADD)GetProcAddress(hDll, "Add");
    PFN_SUB pSub = (PFN_SUB)GetProcAddress(hDll, "Sub");
    if (!pAdd || !pSub) { printf("GetProcAddress failed\n"); FreeLibrary(hDll); return 1; }

    printf("Add: %d\n", pAdd(5, 3));
    printf("Sub: %d\n", pSub(5, 3));

    FreeLibrary(hDll);
    return 0;
}

進階:若 .def 指定序數,可用 `GetProcAddress(hDll, MAKEINTRESOURCE(1))` 依序數取得,速度較快但需維護對應表。

四、MFC Regular DLL 關鍵差異

  1. 專案類型選 **MFC AppWizard (dll)** → Regular DLL using shared MFC DLL
  2. 匯出函式**必須**以 `AFX_MANAGE_STATE(AfxGetStaticModuleState())` 開頭,切換模組狀態
  3. 匯出 C++ 類別:類別宣告加 `__declspec(dllexport)`,成員函式同理
  4. 提供 `.h`、`.lib`、`.dll` 三檔給呼叫端
// MFC Regular DLL 匯出函式範例
extern "C" __declspec(dllexport) int MyFunc(int x) {
    AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 關鍵!
    // 使用 MFC 類別 (CString, CFile, etc.)
    return x * 2;
}

五、Extension DLL 要點

  • 無 `CWinApp` 派生物件,需在 `DllMain` 手動初始化/清理 MFC
  • 匯出完整 C++ 類別 (含虛函式、動態建立)
  • 僅供 MFC 應用呼叫,需連結相同版本 MFC DLL

六、常見問題排查

  1. DLL 找不到:檢查 `PATH`、應用程式目錄、`System32`、依賴走向 (Dependency Walker)
  2. 呼叫慣例不符:確認 `__cdecl`/`__stdcall` 一致,預設 C/C++ 為 `__cdecl`,WinAPI 為 `__stdcall`
  3. C++ 名稱修飾:`extern "C"` 只對函式有效,類別匯出需 `__declspec(dllexport)` 類別本身
  4. CRT 版本衝突:DLL 與 EXE 需使用相同 CRT 版本 (/MD /MT 一致),否則 `new`/`delete`、檔案控制代碼跨界會崩潰
  5. Debug/Release 混用:嚴禁混用,記憶體配置器不相容

七、實戰範例:MFC 對話方塊專案顯式呼叫 Non-MFC DLL

本文附帶範例專案 `DLL` (Non-MFC DLL) 與 `Test` (MFC Dialog),`Test` 以 `LoadLibrary`/`GetProcAddress` 呼叫 `DLL` 匯出的 `Test(int i)` 實現 `++i` 功能。完整程式碼見原文。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved