實現 Win32 程序的消息映射宏(類似 MFC )


  對於消息映射宏,不用多說了,用過 MFC 的人都很清楚。但目前有不少程序由於各種原因並沒有使用 MFC,所以本帖討論一下如何在 Win32 程序中實現類似MFC的消息映射宏。其實 Windows 的頭文件 “WindowsX.h”(注意:不是“Windows.h”) 中提供了一些有用的宏來幫助我們實現消息映射。本座是也基於這個頭文件實現消息映射,首先看看宏定義文件:

 

#pragma once

#include <windowsx.h>

/************************************************************************/
/* 消息映射幫助宏 */
/************************************************************************/

/* see: WindowsX.h */
#define HANDLE_SYS_MSG(hwnd, message, fn) HANDLE_MSG(hwnd, message, fn)

/* LRESULT Cls_OnMessage(HWND hwnd, WPARAM wParam, LPARAM lParam) */
#define HANDLE_USER_MSG(hwnd, message, fn) \
case (message): return (LRESULT)(fn)((hwnd), (wParam), (lParam))

#define FORWARD_USER_MSG(hwnd, message, wParam, lParam, fn) \
(LRESULT)(fn)((hwnd), (message), (wParam), (lParam))

#define GET_WND_PROC_INTERNAL(theClass, flag) ((WNDPROC)theClass##flag##WndProc)
#define GET_DLG_PROC_INTERNAL(theClass, flag) ((DLGPROC)theClass##flag##DlgProc)

#define DECLARE_MSG_MAP_INTERNAL(theClass, flag) \
static LRESULT CALLBACK theClass##flag##WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

#define DECLARE_DLG_MSG_MAP_INTERNAL(theClass, flag) \
static BOOL CALLBACK theClass##flag##DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

#define BEGIN_MSG_MAP_INTERNAL(theClass, flag) \
LRESULT theClass##flag##WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) \
{ \
LRESULT result = 0; \
\
switch(msg) \
{

#define BEGIN_DLG_MSG_MAP_INTERNAL(theClass, flag) \
BOOL theClass##flag##DlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) \
{ \
BOOL retVal = TRUE; \
LRESULT result = 0; \
\
switch(msg) \
{

// 窗口過程為類中的靜態成員函數
#define GET_WND_PROC(theClass) GET_WND_PROC_INTERNAL(theClass, ::)
#define GET_DLG_PROC(theClass) GET_DLG_PROC_INTERNAL(theClass, ::)

#define DECLARE_MSG_MAP(theClass) \
public: \
DECLARE_MSG_MAP_INTERNAL(theClass, ::)

#define DECLARE_DLG_MSG_MAP(theClass) \
public: \
DECLARE_DLG_MSG_MAP_INTERNAL(theClass, ::)

#define BEGIN_MSG_MAP(theClass) BEGIN_MSG_MAP_INTERNAL(theClass, ::)
#define BEGIN_DLG_MSG_MAP(theClass) BEGIN_DLG_MSG_MAP_INTERNAL(theClass, ::)

/* 消息處理函數的聲明請參考: <WindowsX.h> 的 HANDLE_MSG */
#define ADD_MSG_MAP(msg, fn) \
case (msg): result = HANDLE_##msg((hWnd), (wParam), (lParam), (fn)); break;

/* LRESULT Cls_OnMessage(HWND hwnd, WPARAM wParam, LPARAM lParam) */
#define ADD_USER_MSG_MAP(msg, fn) \
case (msg): result = (LRESULT)(fn)((hWnd), (wParam), (lParam)); break;

#define END_MSG_MAP() \
default: \
result = ::DefWindowProc(hWnd, msg, wParam, lParam); \
} \
\
return result; \
}

#define END_DLG_MSG_MAP() \
default: \
retVal = FALSE; \
} \
\
if(retVal) \
SetDlgMsgResult(hWnd, msg, result); \
\
return retVal; \
}

// 窗口過程為全局函數
#define GET_GLOBAL_WND_PROC(theClass) GET_WND_PROC_INTERNAL(theClass, _)
#define DECLARE_GLOBAL_MSG_MAP(theClass) DECLARE_MSG_MAP_INTERNAL(theClass, _)
#define BEGIN_GLOBAL_MSG_MAP(theClass) BEGIN_MSG_MAP_INTERNAL(theClass, _)
#define END_GLOBAL_MSG_MAP() END_MSG_MAP()

#define GET_GLOBAL_DLG_PROC(theClass) GET_DLG_PROC_INTERNAL(theClass, _)
#define DECLARE_GLOBAL_DLG_MSG_MAP(theClass) DECLARE_DLG_MSG_MAP_INTERNAL(theClass, _)
#define BEGIN_GLOBAL_DLG_MSG_MAP(theClass) BEGIN_DLG_MSG_MAP_INTERNAL(theClass, _)
#define END_GLOBAL_DLG_MSG_MAP() END_DLG_MSG_MAP()

// 綁定對象指針到窗口
#define ATTACH_OBJ_PTR_TO_WINDOW(hwnd, objPtr) ::SetWindowLong(hwnd, GWL_USERDATA, (LONG_PTR)objPtr)
#define GET_OBJ_PTR_FROM_WINDOW(hwnd, theClass) (theClass*)(LONG_PTR)::GetWindowLong(hwnd, GWL_USERDATA)

#define DEFINE_OBJ_PTR_FROM_WINDOW(hwnd, theClass, pObj) \
theClass* pObj = (theClass*)(LONG_PTR)::GetWindowLong(hwnd, GWL_USERDATA); \
ASSERT(pObj);

 

  先介紹一下幾個重要的宏定義:

  • DECLARE_MSG_MAP(theClass):聲明窗口過程函數,其中窗口過程函數實現為類的靜態方法
  • DECLARE_GLOBAL_MSG_MAP(theClass):聲明窗口過程函數,其中窗口過程函數實現為全局函數,因此“theClass”參數可以任意寫,不一定是類名
  • DECLARE_DLG_MSG_MAP(theClass):聲明對話框的窗口過程函數,其中窗口過程函數實現為類的靜態方法
  • DECLARE_GLOBAL_DLG_MSG_MAP(theClass):聲明對話框窗口過程函數,其中窗口過程函數實現為全局函數,因此“theClass”參數可以任意寫,不一定是類名
  • BEGIN_MSG_MAP(theClass):定義窗口過程函數,其中窗口過程函數實現為類的靜態方法
  • BEGIN_GLOBAL_MSG_MAP(theClass):定義窗口過程函數,其中窗口過程函數實現為全局函數,因此“theClass”參數可以任意寫,不一定是類名
  • BEGIN_DLG_MSG_MAP(theClass):定義對話框的窗口過程函數,其中窗口過程函數實現為類的靜態方法
  • BEGIN_GLOBAL_DLG_MSG_MAP(theClass):定義對話框窗口過程函數,其中窗口過程函數實現為全局函數,因此“theClass”參數可以任意寫,不一定是類名
  • ADD_MSG_MAP(msg, fn):添加 Windows 內部消息映射
  • ADD_USER_MSG_MAP(msg, fn):添加用戶自定義消息映射
  • END_MSG_MAP():結束消息映射,對應 BEGIN_MSG_MAP
  • END_GLOBAL_MSG_MAP():結束消息映射,對應 BEGIN_GLOBAL_MSG_MAP
  • END_DLG_MSG_MAP():結束消息映射,對應 BEGIN_DLG_MSG_MAP
  • END_GLOBAL_DLG_MSG_MAP():結束消息映射,對應 BEGIN_GLOBAL_DLG_MSG_MAP
  • GET_WND_PROC(theClass):獲取窗口過程函數的地址,對應 DECLARE_MSG_MAP
  • GET_GLOBAL_WND_PROC(theClass):獲取窗口過程函數的地址,對應 DECLARE_GLOBAL_MSG_MAP
  • GET_DLG_PROC(theClass):獲取對話框窗口過程函數的地址,對應 DECLARE_DLG_MSG_MAP
  • GET_GLOBAL_DLG_PROC(theClass):獲取對話框窗口過程函數的地址,對應 DECLARE_GLOBAL_DLG_MSG_MAP
  • ATTACH_OBJ_PTR_TO_WINDOW(hwnd, objPtr):把對象指針與窗口句柄進行綁定
  • GET_OBJ_PTR_FROM_WINDOW(hwnd, theClass):從窗口句柄中獲取對象指針
  • DEFINE_OBJ_PTR_FROM_WINDOW(hwnd, theClass, pObj):從窗口句柄中獲取對象指針,並賦值給局部變量 pObj

   這里說明一下:對話框的消息映射與普通窗口的消息映射使用不同的宏進行定義;另外,窗口過程可以實現為類的靜態方法或全局函數。例如,如果要定義一個對話框的窗口過程,並實現為全局函數則使用 DECLARE_GLOBAL_DLG_MSG_MAPBEGIN_GLOBAL_DLG_MSG_MAP、END_GLOBAL_DLG_MSG_MAP GET_GLOBAL_DLG_PROC 系列宏。

 

  下面以一個普通窗口的消息映射為例子演示如何使用這些宏:

/*** MyClass.h ***/
class MyClass
{
  // 其它方法
  virtual void OnDraw(const paint_dc& dc);
  virtual BOOL Destroy();
  // 系統消息
static BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
static void OnDestroy(HWND hwnd);
static void OnPaint(HWND hWnd);
static void OnClose(HWND hwnd);
static void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
static void OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags);
static void OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
static void OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized);
  // 用戶自定義消息
static LRESULT OnLockScreen(HWND hwnd, WPARAM wParam, LPARAM lParam);
  static LRESULT OnMenuBtnDown(HWND hwnd, WPARAM wParam, LPARAM lParam);
static LRESULT OnSensorUp(HWND hwnd, WPARAM wParam, LPARAM lParam);
static LRESULT OnSensorDown(HWND hwnd, WPARAM wParam, LPARAM lParam);
static LRESULT OnSensorLeft(HWND hwnd, WPARAM wParam, LPARAM lParam);
static LRESULT OnSensorRight(HWND hwnd, WPARAM wParam, LPARAM lParam);
  // 聲明窗口過程
  DECLARE_MSG_MAP(MyClass)
};
/*** MyClass.cpp ***/

#include "MyClass.h"
// 定義消息映射
BEGIN_MSG_MAP(MyClass)
ADD_MSG_MAP(WM_CREATE, OnCreate)
ADD_MSG_MAP(WM_CLOSE, OnClose)
ADD_MSG_MAP(WM_DESTROY, OnDestroy)
ADD_MSG_MAP(WM_PAINT, OnPaint)
ADD_MSG_MAP(WM_LBUTTONDOWN, OnLButtonDown)
ADD_MSG_MAP(WM_LBUTTONUP, OnLButtonUp)
ADD_MSG_MAP(WM_MOUSEMOVE, OnMouseMove)
ADD_MSG_MAP(WM_ACTIVATE, OnActivate)
ADD_USER_MSG_MAP(MSG_MENU_BTN_DOWN, OnMenuBtnDown)
ADD_USER_MSG_MAP(MSG_SENSOR_UP, OnSensorUp)
ADD_USER_MSG_MAP(MSG_SENSOR_DOWN, OnSensorDown)
ADD_USER_MSG_MAP(MSG_SENSOR_LEFT, OnSensorLeft)
ADD_USER_MSG_MAP(MSG_SENSOR_RIGHT, OnSensorRight)
ADD_USER_MSG_MAP(SHELL_MSG_LOCK_SCREEN, OnLockScreen)
END_MSG_MAP()
// 實現消息處理函數
BOOL MyClass::OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
  // 把 lpCreateStruct->lpCreateParams 綁定到 hwnd。
  // 通常 lpCreateStruct->lpCreateParams 設置 MyClass 對象的 this 指針,在 ::CreateWindowEx() 函數中指定。
ATTACH_OBJ_PTR_TO_WINDOW(hwnd, lpCreateStruct->lpCreateParams);

return TRUE;
}

void MyClass::OnClose(HWND hwnd)
{
  // 獲取 hwnd 綁定的對象指針,並賦值給局部變量 pvShell
DEFINE_OBJ_PTR_FROM_WINDOW(hwnd, MyClass, pvShell);

pvShell->Destroy();
}

void MyClass::OnDestroy(HWND hwnd)
{
::PostQuitMessage(0);
}

void MyClass::OnPaint(HWND hwnd)
{
  // 獲取 hwnd 綁定的對象指針,並賦值給局部變量 pvShell 
DEFINE_OBJ_PTR_FROM_WINDOW(hwnd, MyClass, pvShell);

paint_dc dc(hwnd);
pvShell->OnDraw(dc);
}

void MyClass::OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
// ...
}

void MyClass::OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
{
// ...
}

// 其它消息處理方法
// 。。。。。。


  重要說明:不知大家是否注意到,我們的消息處理函數與 MFC 的消息處理函數是有區別的。區別就在於我們的消息處理函數是 static 類型的,而 MFC 的消息處理函數則不是。因此,MFC 的消息處理函數很容易獲得 this 指針,而我們的函數就沒那么直接了,因此需要使用了比較迂回的方法獲取 this 指針,具體方法是:

  1. 在 ::CreateWindowEx(... , lpParam) 方法中,把 MyClass 的 this 指針作為參數傳入。
  2.  處理 WM_CREATE 消息時調用 ATTACH_OBJ_PTR_TO_WINDOW(hwnd, lpCreateStruct->lpCreateParams),把 this 指針綁定到 hwnd。
  3. 在其他消息處理方法中用 GET_OBJ_PTR_FROM_WINDOW 或 DEFINE_OBJ_PTR_FROM_WINDOW 獲取 this 指針。

 

CodeProject


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM