实现 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_MAP、BEGIN_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 指针,具体方法是:
- 在 ::CreateWindowEx(... , lpParam) 方法中,把 MyClass 的 this 指针作为参数传入。
- 处理 WM_CREATE 消息时调用 ATTACH_OBJ_PTR_TO_WINDOW(hwnd, lpCreateStruct->lpCreateParams),把 this 指针绑定到 hwnd。
- 在其他消息处理方法中用 GET_OBJ_PTR_FROM_WINDOW 或 DEFINE_OBJ_PTR_FROM_WINDOW 获取 this 指针。
转自:http://www.cnblogs.com/ldcsaa/archive/2012/02/13/2348588.html
实现 Win32 程序的消息映射宏(类似 MFC )的更多相关文章
- (二)一个MFC程序,消息映射,纯代码
1.应用程序类 CWinApp https://docs.microsoft.com/zh-cn/cpp/mfc/reference/cwinapp-class?f1url=https%3A%2F%2 ...
- MFC_1.2 消息映射宏 数据绑定和交换
消息映射宏 有三个主要的宏 类内声明 DECLARE_MESSAGE_MAP 表示使用消息映射 在CPP文件中使用 BEGIN_MESSAGE_MAP 和 END_MESSAGE_MAP 包含对应的消 ...
- 模仿WIN32程序处理消息
#include "stdafx.h" #include "MyMessage.h" #include <conio.h> using namesp ...
- 图解MFC基本框架(深入消息映射机制)
首先,先看整体的消息流向图: 上图解释: 起点是消息循环,在winmain函数中(mfc中winmain函数是隐含的调用的,在app全局对象构造完后紧接着调用winmain函数),while循环中不断 ...
- 消息映射(C++)(转)
摘要:控件通知消息有很多种,但是有一种是很常用,但是又不是很容易掌握的,那就是WM_NOTIFY,我试着对此做一下比较全面的论述,有不对的地方,还希望各路大虾批评指正. 控件通知消息 ...
- MFC消息映射机制以及画线功能实现
---此仅供用于学习交流,切勿用于商业用途,转载请注明http://www.cnblogs.com/mxbs/p/6213404.html. 利用VS2010创建一个单文档标准MFC工程,工程名为Dr ...
- MFC消息映射与命令传递
题外话:刚开始学视窗程序设计的时候,我就打印了一本Windows消息详解,里面列举了各种已定义消息的意义和作用,共10多页,在编程的时候翻翻,有时觉得很受用.我发觉很多编程的朋友,虽然每天都面对消息, ...
- 剖析MFC六大关键技术(五六)--消息映射与命令传递
说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西.有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样.特别是窗口消息及对消息的处理跟以前 ...
- MFC六大核心机制之五、六:消息映射和命令传递
作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...
随机推荐
- Caesars Cipher
让上帝的归上帝,凯撒的归凯撒. 下面我们来介绍风靡全球的凯撒密码Caesar cipher,又叫移位密码. 移位密码也就是密码中的字母会按照指定的数量来做移位. 一个常见的案例就是ROT13密码,字母 ...
- ansible来了
番一.OP酱的自白 自从入了贵圈,每天需要强大的内心来维护混乱的线上,每天都是用浆糊一样的shell /python在糊墙补窟窿啊,感觉每天都是在和if else打交道啊,每次花牛鼻子劲写的 ...
- # 2018-2019-2 20165210《网络攻防技术》Exp1 PC平台逆向破解(BOF实验)
2018-2019-2 20165210<网络攻防技术>Exp1 PC平台逆向破解(BOF实验) 实验分为三个部分: 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数. ...
- IIS站点/虚拟目录中访问共享目录(UNC)以及建立后的应用程序的信任级别问题
UNC是 Universal Naming Convention 的简称,也叫通用命名规范.通用命名约定.网络(范指局域网)上资源的完整位置名称.格式为 \\servername\sharenam ...
- win10 安装MongoDB
win10上面安装mongodb的时候,注意不要勾选上Install MongoDB Compass, 否则会退出报错!!!! mongodb的安装 我是在E盘建立的一个mongodb文件夹,用来安装 ...
- geotools中泰森多边形的生成
概述 本文讲述如何在geotools中生成泰森多边形,并shp输出. 泰森多边形 1.定义 泰森多边形又叫冯洛诺伊图(Voronoi diagram),得名于Georgy Voronoi,是由一组由连 ...
- [转载]Unicode中对中文字符的编码
以前写过一篇贴子是写中文在unicode中的编码范围 unicode中文范围,但写的不是很详细,今天再次研究了下unicode,并给出详细的unicode取值范围. 本次研究的unicode对象是un ...
- php序号发生器,数字重组,可以隐藏原来的1,2,3。。。
一个晚上的成果,原理: 将1,2,3,4,5,6,7,8,9,0映射到9,5,1,0,4,8,7,3,2,6 同理映射base,base有1-10种数组,也就是可以一位数到10位数 $base 实际上 ...
- Spring配置--tx事务配置方式
前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的. ...
- Codeforces 815C. Karen and Supermarket【树形DP】
LINK 思路 首先发现依赖关系是一个树形的结构 然后因为直接算花多少钱来统计贡献不是很好 因为数组开不下 那就可以算一个子树里面选多少个的最小代价就可以了 注意统计贡献的时候用优惠券的答案只能在1号 ...