MFC 的消息映射機制是視窗程式處理 Windows 訊息的核心。它避免了傳統虛函數表帶來的記憶體浪費,透過查表方式將訊息與處理函數關聯。以下以 CDrawView 類別響應滑鼠左鍵按下訊息 (WM_LBUTTONDOWN) 為例,說明完整流程。
在 ClassWizard 維護的註釋巨集區塊中宣告函數原型:
//{{AFX_MSG(CDrawView)
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
//}}AFX_MSG
afx_msg 巨集標識這是一個訊息處理函數,註釋巨集讓 ClassWizard 能識別並維護此處代碼。
在 BEGIN_MESSAGE_MAP 與 END_MESSAGE_MAP 巨集之間建立映射關係:
BEGIN_MESSAGE_MAP(CDrawView, CView)
//{{AFX_MSG_MAP(CDrawView)
ON_WM_LBUTTONDOWN()
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
ON_WM_LBUTTONDOWN() 巨集將 WM_LBUTTONDOWN 訊息與 OnLButtonDown 函數綁定。其內部定義包含訊息碼、參數類型標誌及函數指標轉換,確保訊息到達時自動調用對應處理函數。
ClassWizard 自動生成函數骨架,開發者填入具體邏輯:
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此加入訊息處理程式碼和/或呼叫預設值
CView::OnLButtonDown(nFlags, point);
}
新增一個訊息響應需修改三處:標頭檔宣告、映射表項目、函數實作。
若在基類為每種訊息定義虛函數,所有衍生類都將繼承龐大的虛函數表,嚴重浪費記憶體。MFC 採用消息映射機制解決此問題。
HWND 到 CWnd* 的對照表。HWND 查找對應的 C++ 物件指標。WindowProc(),進而呼叫 OnWndMsg()。OnWndMsg() 根據訊息類型查找消息映射表(即標頭檔註釋巨集區塊與原始檔 BEGIN_MESSAGE_MAP 區塊)。關鍵虛函數簽名:
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
HDC hdc = ::GetDC(m_hWnd);
MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL);
LineTo(hdc, point.x, point.y);
::ReleaseDC(m_hWnd, hdc);
CDC* pDC = GetDC();
pDC->MoveTo(m_ptOrigin);
pDC->LineTo(point);
ReleaseDC(pDC);
CClientDC dc(this); // 建構時自動 GetDC,解構時自動 ReleaseDC
dc.MoveTo(m_ptOrigin);
dc.LineTo(point);
CClientDC 封裝了獲取與釋放邏輯,程式碼更簡潔安全,是 MFC 繪圖的推薦做法。