將「消息 ID → 處理函數」映射表達為靜態數組,運行時查表分發,實現開閉原則:新增消息只需擴展表,核心分發邏輯不變。這正是 MFC 消息映射、Windows 內核 IRP 分發、嵌入式狀態機的核心模式。
#include <windows.h> // 類型定義
// 消息映射表項
typedef struct {
UINT message; // 消息 ID
LRESULT (*handler)(HWND, UINT, WPARAM, LPARAM); // 處理函數指針
} MSG_MAP_ENTRY;
// 宏:計算靜態數組元素個數
#define DIM(x) (sizeof(x) / sizeof((x)[0]))
// 窗口消息映射表
MSG_MAP_ENTRY g_msgMap[] = {
{ WM_CREATE, OnCreate },
{ WM_PAINT, OnPaint },
{ WM_SIZE, OnSize },
{ WM_COMMAND, OnCommand }, // 命令消息二次分發
{ WM_SETFOCUS, OnSetFocus },
{ WM_CLOSE, OnClose },
{ WM_DESTROY, OnDestroy },
{ 0, NULL } // 結束標記(可選)
};
// 命令消息映射表(WM_COMMAND 的 wParam 低字)
MSG_MAP_ENTRY g_cmdMap[] = {
{ IDM_ABOUT, OnAbout },
{ IDM_OPEN, OnFileOpen },
{ IDM_SAVEAS, OnSaveAs },
{ 0, NULL }
};
// 主窗口過程
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
for (int i = 0; g_msgMap[i].handler; ++i) {
if (msg == g_msgMap[i].message)
return g_msgMap[i].handler(hwnd, msg, wParam, lParam);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// WM_COMMAND 專用分發
LRESULT CALLBACK OnCommand(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
UINT cmdId = LOWORD(wParam);
for (int i = 0; g_cmdMap[i].handler; ++i) {
if (cmdId == g_cmdMap[i].message)
return g_cmdMap[i].handler(hwnd, msg, wParam, lParam);
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
LRESULT OnCreate(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
// 初始化資源
return 0;
}
LRESULT OnPaint(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 10, 10, L"Hello, Table-Driven!", 20);
EndPaint(hwnd, &ps);
return 0;
}
LRESULT OnAbout(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
MessageBox(hwnd, L"Table-Driven Demo", L"About", MB_OK);
return 0;
}
// ... 其他處理函數同理
typedef struct {
UINT message;
LRESULT (*handler)(HWND, UINT, WPARAM, LPARAM);
int priority; // 數值越大越優先
void* userData; // 攜帶上下文
} DynMsgEntry;
#define MAX_DYN_HANDLERS 64
static DynMsgEntry g_dynHandlers[MAX_DYN_HANDLERS];
static int g_dynCount = 0;
BOOL RegisterMsgHandler(UINT msg, LRESULT (*fn)(HWND,UINT,WPARAM,LPARAM), int priority, void* data) {
if (g_dynCount >= MAX_DYN_HANDLERS) return FALSE;
g_dynHandlers[g_dynCount++] = (DynMsgEntry){msg, fn, priority, data};
// 可按 priority 排序,或查找時遍歷選擇最高優先級
return TRUE;
}
LRESULT CALLBACK DynamicWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
LRESULT (*bestHandler)(HWND,UINT,WPARAM,LPARAM) = NULL;
int bestPri = -1;
for (int i = 0; i < g_dynCount; ++i) {
if (g_dynHandlers[i].message == msg && g_dynHandlers[i].priority > bestPri) {
bestPri = g_dynHandlers[i].priority;
bestHandler = g_dynHandlers[i].handler;
}
}
if (bestHandler) return bestHandler(hwnd, msg, wp, lp);
return DefWindowProc(hwnd, msg, wp, lp);
}
| 本文模式 | MFC 對應 | 說明 |
|---|---|---|
MSG_MAP_ENTRY | AFX_MSGMAP_ENTRY | 結構體定義基本一致 |
靜態數組 g_msgMap[] | BEGIN_MESSAGE_MAP / ON_MESSAGE / END_MESSAGE_MAP | 宏展開生成靜態數組 |
WndProc 循環查表 | CWnd::WindowProc -> OnWndMsg -> AfxFindMessageEntry | MFC 額外支援命令路由、消息反射 |
命令二次分發 OnCommand | ON_COMMAND / ON_BN_CLICKED | MFC 用 CCmdTarget 鏈路由 |
nginx、libevent)。state × event → action + next_state 二維表。表驅動設計將控制邏輯數據化,以極小的運行時開銷換取極大的靈活性。從 Windows 窗口過程到現代 Web 框架路由,均是此模式的變體。掌握其核心——靜態表定義 + 通用分發循環 + 可擴展註冊機制,即可在任何 C/C++ 專案中構建清晰、可維護的分發層。