DLL (Dynamic Link Library) 實現程式碼共享、模組化、獨立升級。多進程共享記憶體映像,減少體積;介面不變時,僅替換 DLL 即可升級功能。VC++ 支援三類 DLL:Non-MFC、Regular (靜態/動態連結 MFC)、Extension (MFC 擴充)。本文以 Non-MFC 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 類別庫擴充、自訂控件 |
File → New → Project → Win32 Dynamic-Link Library,命名 `MyDll`。
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;
}
方式 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`。
特點:啟動時自動載入 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` 目錄。
特點:執行期動態載入/卸載,彈性高、可選擇性載入、無需 `.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 匯出函式範例
extern "C" __declspec(dllexport) int MyFunc(int x) {
AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 關鍵!
// 使用 MFC 類別 (CString, CFile, etc.)
return x * 2;
}
本文附帶範例專案 `DLL` (Non-MFC DLL) 與 `Test` (MFC Dialog),`Test` 以 `LoadLibrary`/`GetProcAddress` 呼叫 `DLL` 匯出的 `Test(int i)` 實現 `++i` 功能。完整程式碼見原文。