对于消息映射宏,不用多说了,用过 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 指针。

转自:http://www.cnblogs.com/ldcsaa/archive/2012/02/13/2348588.html

实现 Win32 程序的消息映射宏(类似 MFC )的更多相关文章

  1. (二)一个MFC程序,消息映射,纯代码

    1.应用程序类 CWinApp https://docs.microsoft.com/zh-cn/cpp/mfc/reference/cwinapp-class?f1url=https%3A%2F%2 ...

  2. MFC_1.2 消息映射宏 数据绑定和交换

    消息映射宏 有三个主要的宏 类内声明 DECLARE_MESSAGE_MAP 表示使用消息映射 在CPP文件中使用 BEGIN_MESSAGE_MAP 和 END_MESSAGE_MAP 包含对应的消 ...

  3. 模仿WIN32程序处理消息

    #include "stdafx.h" #include "MyMessage.h" #include <conio.h> using namesp ...

  4. 图解MFC基本框架(深入消息映射机制)

    首先,先看整体的消息流向图: 上图解释: 起点是消息循环,在winmain函数中(mfc中winmain函数是隐含的调用的,在app全局对象构造完后紧接着调用winmain函数),while循环中不断 ...

  5. 消息映射(C++)(转)

    摘要:控件通知消息有很多种,但是有一种是很常用,但是又不是很容易掌握的,那就是WM_NOTIFY,我试着对此做一下比较全面的论述,有不对的地方,还希望各路大虾批评指正.     控件通知消息     ...

  6. MFC消息映射机制以及画线功能实现

    ---此仅供用于学习交流,切勿用于商业用途,转载请注明http://www.cnblogs.com/mxbs/p/6213404.html. 利用VS2010创建一个单文档标准MFC工程,工程名为Dr ...

  7. MFC消息映射与命令传递

    题外话:刚开始学视窗程序设计的时候,我就打印了一本Windows消息详解,里面列举了各种已定义消息的意义和作用,共10多页,在编程的时候翻翻,有时觉得很受用.我发觉很多编程的朋友,虽然每天都面对消息, ...

  8. 剖析MFC六大关键技术(五六)--消息映射与命令传递

    说到消息,在MFC中,“最熟悉的神秘”可算是消息映射,那是我们刚开始接触MFC时就要面对的东西.有过SDK编程经验的朋友转到MFC编程的时候,一下子觉得什么都变了样.特别是窗口消息及对消息的处理跟以前 ...

  9. MFC六大核心机制之五、六:消息映射和命令传递

    作为C++程序员,我们总是希望自己程序的所有代码都是自己写出来的,如果使用了其他的一些库,也总是千方百计想弄清楚其中的类和函数的原理,否则就会感觉不踏实.所以,我们对于在进行MFC视窗程序设计时经常要 ...

随机推荐

  1. 【前端工具】 git windows下搭建全过程

    1. Git,Windows下的Git,地址:http://msysgit.googlecode.com/files/Git-1.7.9-preview20120201.exe(方便下载) 2 .SS ...

  2. UI-基本控件的简单使用

    1.IBAction:   //====================== 1> 能保证方法可以连线 2> 相当于void 2.IBOutlet: 1> 能保证属性可以连线 3.常 ...

  3. 201621123005《Java程序设计》第二周学习总结

    201621123005<JAVA程序设计>第二周学习总结 1. 本周学习总结 本章学习了String 的不可变性.自动装箱和拆箱过程,并熟悉了动态数组等 Java中的应用,还有Array ...

  4. Pavilion M4-1016TX 加装固态硬盘(SSD)+UEFI+GPT安装WIN8.1

    折腾了一天,终于将电脑加上SSD和装上系统,记录下,方便后面忘记使用. 步骤: 1.Pavilion M4-1016TX内置了mSata的接口,大小是全高的.ssd支持大小官方说法是测试过32g的,目 ...

  5. APUE学习笔记——10.18 system函数 与waitpid

    system函数 system函数用方便在一个进程中执行命令行(一行shell命令). 用法如下: #include <stdio.h> #include <stdlib.h> ...

  6. 2018.11.15 RF antenna impedance-matching

    We have studied the impedance-matching of RF transmission line between the antenna and the RX / TX m ...

  7. New Concept English three (23)

    31w 45 People become quite illogical when they try to decide what can be eaten and what cannot be ea ...

  8. 设计 react 组件

    重新设计 React 组件库 诚身 7 个月前   在 react + redux 已经成为大部分前端项目底层架构的今天, 让我们再次回到软件工程界一个永恒问题的探讨上来, 那就是如何提升一个开发团队 ...

  9. react 入门的好东西 可以做出一个完整的网站

    链接  (包含了antd 组件的使用) 安装依赖报错问题                            可能需要按顺序安装,  不能cnpm npm 混合安装,  参考这个package.js ...

  10. 在.NET Core中连接使用Zookeeper

    一开始找到的是ZookeeperNetEx,但是很多API都很原始,不怎么好用. 最后确定用Rabbit.Zookeeper来做,他对ZookeeperNetEx进行了封装,要简单不少. 和c语言和j ...