首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

VC++ 非 MFC DLL 開發完整教學:從建立到導出函數、變數、類別

     編輯:更多程式設計知識

非 MFC DLL 不依賴 MFC 類別庫,體積小、相依性低,適合跨語言、跨平台模組。本文以 VC++ 6.0 Win32 Dynamic-Link Library 專案為例,完整示範建立、導出函數/變數/類別、靜態/動態呼叫、DllMain、呼叫慣例等核心實作。

1. 建立最簡單的 DLL

  1. New Project → **Win32 Dynamic-Link Library**,名稱 `DllTest`,**勿選** MFC AppWizard。
  2. 新增 `Lib.h`、`Lib.cpp`:
// Lib.h
#ifndef LIB_H
#define LIB_H
extern "C" __declspec(dllexport) int add(int x, int y);
#endif

// Lib.cpp
#include "Lib.h"
int add(int x, int y) { return x + y; }

2. 動態載入呼叫端

#include 
#include 
typedef int(*LPADD)(int, int);
int main() {
    HINSTANCE hDll = LoadLibrary("..\\Debug\\DllTest.dll");
    if (hDll) {
        LPADD pAdd = (LPADD)GetProcAddress(hDll, "add");
        if (pAdd) printf("%d", pAdd(2,3));
        FreeLibrary(hDll);
    }
    return 0;
}

流程:`LoadLibrary` 載入 → `GetProcAddress` 取函數位址 → 函數指標呼叫 → `FreeLibrary` 釋放。

3. 導出函數第二種方式:.def 檔

; Lib.def
LIBRARY DllTest
EXPORTS
    add @1

序數 `@1` 允許 `GetProcAddress(hDll, MAKEINTRESOURCE(1))` 以整數取得,比字串查找略快。

4. 靜態連結呼叫端

  1. 將 DLL 建置產生的 `DllTest.lib`、`DllTest.dll` 複製至呼叫端專案目錄。
  2. 呼叫端程式碼:
#pragma comment(lib, "DllTest.lib")
extern "C" __declspec(dllimport) int add(int x, int y);
int main() { printf("%d", add(2,3)); }

`.lib` 為導入庫,連結時嵌入 DLL 檔名與符號資訊,載入器自動完成載入與繫結。

5. DllMain 實作與四大事件

BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID reserved) {
    switch (reason) {
        case DLL_PROCESS_ATTACH:  printf("process attach\n"); break;
        case DLL_THREAD_ATTACH:   printf("thread attach\n"); break;
        case DLL_THREAD_DETACH:   printf("thread detach\n"); break;
        case DLL_PROCESS_DETACH:  printf("process detach\n"); break;
    }
    return TRUE;
}

驗證序列:`LoadLibrary` → `PROCESS_ATTACH` → 呼叫函數 → `FreeLibrary` → `PROCESS_DETACH`。

6. 呼叫慣例 __stdcall 實戰

DLL 端聲明:

extern "C" __declspec(dllexport) int __stdcall add(int x, int y);

呼叫端函數指標:

typedef int (__stdcall *LPADD)(int, int);

若雙邊不一致(DLL 為 `__stdcall`,呼叫端漏寫 `__stdcall`),執行時拋出「Run-Time Check Failure #0 - ESP not saved」錯誤。

7. 導出全域變數

.def 宣告

EXPORTS
    g_var DATA

呼叫端兩種引用方式

// 方式一:引用地址 (舊式)
extern int g_var;
printf("%d", *(int*)&g_var);  // 必須取址解參考

// 方式二:__declspec(dllimport) 直接引用值 (推薦)
extern __declspec(dllimport) int g_var;
printf("%d", g_var);
g_var = 200;  // 直接賦值

8. 導出 C++ 類別

標頭檔宏切換:

#ifdef DLL_BUILD
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

class DLL_API Point {
public:
    float x, y;
    Point(float x=0, float y=0) : x(x), y(y) {}
};

class DLL_API Circle {
    Point center;
    float radius;
public:
    void SetCenter(const Point& p) { center = p; }
    void SetRadius(float r) { radius = r; }
    float Area() { return 3.14159f * radius * radius; }
};

DLL 專案定義 `DLL_BUILD`,呼叫端直接包含標頭檔並連結 `.lib` 即可像使用本地類別一樣使用。

總結

非 MFC DLL 開發核心在於:正確導出介面(`dllexport`/`.def`)、選擇適合架構的呼叫方式(靜態/動態)、統一呼叫慣例(`__stdcall`)、善用 `dllimport` 簡化變數/類別引用。掌握這些即可建構健壯、可跨語言呼叫的原生 Windows DLL。

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