The Nine

The Nine 是一名非常热爱编程以及旅游、心理学的青年,Delphi将是The Nine的主要编程语言,如果您的爱好和The Nine相近,那么请关注,如果您需要与我联系,请点击"发信给作者"我会及时和你联系!

实现 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);
staticvoid OnDestroy(HWND hwnd);
staticvoid OnPaint(HWND hWnd);
staticvoid OnClose(HWND hwnd);
staticvoid OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
staticvoid OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags);
staticvoid OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
staticvoid 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 指针。


评论
热度(2)

© The Nine | Powered by LOFTER