MFC编程入门之五(MFC消息映射机制概述)

在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作。
比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应。

一、什么是消息?
   窗口消息一般由三个部分组成:1、一个无符号整数,是消息值;2、消息附带的WPARAM类型的参数;3、消
息附带的LPARAM类型的参数。其实,我们一般所说的消息是侠义上的消息值,也就是一个无符号整数,经常被定义为宏。

二、什么是消息映射机制?
   MFC使用一种消息映射机制来处理消息,在应用程序框架中的表现就是一个消息与消息处理函数一一对应的消息映射
表,以及消息处理函数的声明和实现等代码。当窗口接收到消息时,会到消息映射表中查找该消息对应的消息处理函数,
然后由消息处理函数进行相应的处理。

三、Windows消息分类
   Windows消息分为系统消息和用户自定义消息。Windows系统消息有三种:
   1、标准Windows消息。除WM_COMMAND以外WM_开头的消息是标准消息。例如,WM_CREATE、WM_CLOSE。
   2、命令消息。消息名为WM_COMMAND,消息中附带了标识符ID来区分是来自哪个菜单、工具栏按钮或加速键的消息。
   3、通知消息。通知消息一般由列表框等子窗口发送给父窗口,消息名称也是WM_COMMAND,其中附带了控件通知码
来区分控件。
   CWnd的派生类都可以接收到标准Windows消息、通知消息和命令消息。命令消息还可以由文档类等接收。
 用户自定义消息实际上就是用户定义一个宏作为消息,此宏的值应该大于等于WM_USER,然后此宏就可以跟系统消息
一样使用,窗口类中可以定义它的处理函数。

四、消息映射表
   除了一些没有基类的类或CObject的直接派生类外,其他的类都可以自动生成消息映射表。下面的讲解都以CMainFrame
为例。消息映射表如下:
C++代码:
 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx)
    ON_WM_CREATE()
    ON_COMMAND(ID_VIEW_CUSTOMIZE, &CMainFrame::OnViewCustomize)
  
    ON_REGISTERED_MESSAGE(AFX_WM_CREATETOOLBAR, &CMainFrame:OnToolbarCreateNew)
    ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnApplicationLook)
    ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_WINDOWS_7, &CMainFrame::OnUpdateApplicationLook)
    ON_WM_SETTINGCHANGE()
 END_MESSAGE_MAP()
   在BEGIN_MESSAGE_MAG和END_MESSAGE_MAP之间的内容成为消息映射入口项。消息映射除了在CMainFrame的实线文件中
添加消息映射表外,在类的定义文件MainFrame.h中还会添加一个宏调用:
   DECLARE_MESSAGE_MAP()
一般这个宏调用写在类定义的结尾处。

五、添加消息处理函数
   不管是自动添加还是手动添加都有三个步骤:
   1、在类定义中加入消息处理函数的函数声明,注意要以afx_msg打头。例如MainFrame.h中WM_CREATE的消息处理函数
声明:afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
   2、在类的消息映射表中添加该消息的消息映射入口项。例如WM_CREATE的消息映射入口项:ON_WM_CREATE()。
   3、在类的实现中添加消息处理函数的函数实现。例如,MainFrm.cpp中WM_CREATE的消息处理函数的实现:
   int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
   {
      ......
   }
   通过以上三个步骤以后,WM_CREATE等消息就可以在窗口类中被消息处理函数处理了。

六、各种Windows消息的消息处理函数
   标准Windows消息的消息处理函数都与WM_CREATE消息类似。
   命令消息的消息映射入口项形式如:ON_COMMAND(ID_VIEW_CUSTOMIZE, &CMainFrame::OnViewCustomize),消息为
ID_VIEW_CUSTOMIZE,消息处理函数为OnViewCustomize。
   如果想要使用某个处理函数批量处理某些命令消息,则可以像CMainFrame消息映射表中的ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, 
ID_VIEW_APPLOOK_WINDWOS_7, &CMainFrame::OnApplicationLook)一样添加消息映射入口项,这样值在ID_VIEW_APPLOOK_WIN_2000到ID_VIEW_
APPLOOK_WINDOWS_7之间的菜单项等的命令消息都由CMainFrame的OnApplicationLook函数处理。函数原型为afx_msg void OnApplicationLook
(UINT id);参数id为用户操作的菜单项等的ID。
   在操作列表框等控件时往往会给父窗口发送WM_NOTIFY通知消息。WM_NOTIFY消息的wParam参数为发送通知消息的控件的ID,lParam参数指向
一个结构体,可能是NMHDR结构体,也可能是第一个元素为NMHDR结构体变量的其他结构体。NMHDR结构体的定义如下(仅作了解):
   Typedef struct tagNMHDR
   {
      HWND hwndFrom;
      UINT idFrom;
      UINT code;
   } NMHDR;
 hwndFrom为发送通知消息控件的句柄,idFrom为控件ID,code为要处理的通知消息的通知码,例如NM_CLICk。
 通知消息的消息映射入口项形式如:
   ON_NOTIFY(wNotifyCode, id, memberFxn)
 wNotifyCode为要处理的消息通知码,例如:NM_CLICK。id为控件标识ID。memberFxn为此消息的处理函数。
 通知消息的处理函数的原型为:
   afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT *result);
 如果需要使用用户自定义消息,首先要定义消息宏,如:#define WM_UPDATE_WND(WM_USER+1),再到消息映射表中添加消息映射入口项:
  ON_MESSAGE(WM_UPDATE_WND, &CMainFrame::OnUpdateWnd),然后在MainFrm,h中添加消息处理函数的函数声明:afx_msg LRESULT OnUpdateWnd(
WPARAM wParam, LPARAM lParam);最后在MainFrm.cpp中实现此函数。
   本节对MFC消息映射机制只是做了比较简单的讲解,让大家对它有一定的认识,编程入门这不必强求完全掌握。在以后的教程中会经常涉及
到消息的使用,大家会逐渐熟悉MFC的消息映射机制。

转自:https://www.cnblogs.com/mupiaomiao/p/6183950.html

<think>嗯,用户想深入了解MFC消息映射机制的工作原理,这是个技术深度的问题。之前提供过执行流程的概述,现在需要更深入技术细节。用户引用的四篇资料其实已经勾勒出关键路径:消息映射表的结构(AFX_MSGMAP)、分发流程(WindowProc到DispatchMessage)、宏展开机制。特别是引用3提到的“消息映射的工作流程”三层结构,和引用1中优快云文章提到的原理部分都是核心。技术实现上有三个重点值得展开:第一是消息映射表结构,那些DECLARE_MESSAGE_MAP宏实际上在编译时构建了链表结构。每个CCmdTarget派生类都有自己的消息映射表,运行时通过链表回溯向上级基类查找,这种设计避免了虚函数开销。第二是消息泵(CWinThread::Run)的关键作用。用户可能忽略了这个底层机制——它通过::GetMessage获取消息后,会经过PreTranslateMessage的预处理(比如加速键处理)才进入DispatchMessage。第三是宏的魔法。例如BEGIN_MESSAGE_MAP(CMyView,CView)这种代码展开后实际声明了两个常量数组:messageEntries存放当前类消息和对应的handler,pBaseMap指向父类映射表。这种编译器技巧维持了MFC的简洁接口。对于用户可能的疑问点,比如“为什么普通成员函数能作为消息处理函数”,需要解释thunk机制——MFC在编译时生成静态函数thunk,其函数签名符合WindowProc要求,内部再跳转到成员函数。用户应该需要示例,可以把引用4中的OnCreate调用流程配上示意图:从WM_CREATE进入CMyFrameWnd::WindowProc开始,经过DispatchMessage在映射表中找到OnCreate,最终执行AfxMessageBox的完整过程。安全方面要注意提醒:现代开发中MFC逐渐被Qt等框架取代,但理解其消息机制对掌握Windows编程模型仍有价值。</think> MFC消息映射机制的工作原理详解如下,结合底层代码流程分步解析: --- ### **1. 消息映射机制核心思想** - **核心目的**:无需重写窗口过程函数(`WndProc`),通过宏将Windows消息**自动绑定到类的成员函数**处理。 - **关键技术**:在编译时通过宏生成**静态消息映射表**,运行时通过链表结构回溯查找处理函数。 --- ### **2. 底层实现流程** #### **步骤1:声明消息映射表(头文件)** ```cpp // MyFrame.h class CMyFrameWnd : public CFrameWnd { public: DECLARE_MESSAGE_MAP() // 声明宏:生成消息映射表结构 afx_msg void OnCreate(); // 消息处理函数声明 }; ``` ▶ **作用**:在类声明中插入 `AFX_MSGMAP` 结构体指针和映射表入口定义。 --- #### **步骤2:定义消息映射表(源文件)** ```cpp // MyFrame.cpp BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) // 开始映射 ON_WM_CREATE() // 宏展开:{WM_CREATE, 0, 0, 0, &CMyFrameWnd::OnCreate} END_MESSAGE_MAP() // 结束映射:生成静态表并链接父类映射表 ``` ▶ **宏展开本质**: ```cpp const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const { return &CMyFrameWnd::messageMap; } // 静态消息映射表结构 AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CMyFrameWnd::messageMap = { &CFrameWnd::messageMap, // 链接父类映射表(形成链表) &CMyFrameWnd::_messageEntries[0] // 当前类消息入口 }; // 消息处理函数与消息ID的映射数组 AFX_COMDAT const AFX_MSGMAP_ENTRY CMyFrameWnd::_messageEntries[] = { {WM_CREATE, 0, 0, 0, AfxSig_vv, (AFX_PMSG)&OnCreate}, {0, 0, 0, 0, AfxSig_end, 0} // 结束标记 }; ``` --- #### **步骤3:消息分发流程** 1. **窗口过程入口** Windows系统调用窗口的 `WndProc`(默认为 `AfxWndProc`): ```cpp LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); return AfxCallWndProc(pWnd, hWnd, msg, wp, lp); } ``` 2. **调用DispatchMessage** ```cpp LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { return pWnd->WindowProc(msg, wp, lp); // 转到CWnd::WindowProc } ``` 3. **查表分发消息** ```cpp LRESULT CWnd::WindowProc(UINT msg, WPARAM wp, LPARAM lp) { if (OnWndMsg(msg, wp, lp, &result)) // 尝试处理消息 return result; return DefWindowProc(msg, wp, lp); // 默认处理 } BOOL CWnd::OnWndMsg(UINT msg, WPARAM wp, LPARAM lp, LRESULT* pResult) { const AFX_MSGMAP* pMap = GetMessageMap(); // 获取当前类映射表 while (pMap != NULL) { // 遍历映射表查找匹配的消息ID if (AfxFindMessageEntry(pMap->lpEntries, msg, 0)) { (*lpEntry->pfn)(wp, lp); // 调用处理函数(如OnCreate) return TRUE; } pMap = pMap->pBaseMap; // 回溯父类映射表 } return FALSE; } ``` ▶ **关键点**: - `GetMessageMap()` 返回当前类的静态映射表地址。 - 若当前类未处理消息,则通过 `pMap->pBaseMap` 回溯父类映射表(链表结构)。 --- #### **步骤4:消息处理函数执行** 匹配成功后,直接调用成员函数: ```cpp // 用户定义的处理函数 void CMyFrameWnd::OnCreate() { AfxMessageBox("WM_CREATE Received!"); // 实际业务逻辑 } ``` --- ### **3. 技术优势与本质** | **特性** | **说明** | |--------------------|--------------------------------------------------------------------------| | **编译时绑定** | 宏在编译阶段生成静态映射表,无虚函数开销 | | **运行时回溯** | 通过链表回溯父类映射表,支持消息处理的继承关系 | | **避免虚函数爆炸** | 用静态表替代虚函数表,解决大量消息导致虚函数表膨胀问题 | | **灵活扩展** | 通过 `ON_MESSAGE(自定义消息, 处理函数)` 支持用户自定义消息 | > 例:`ON_WM_CREATE()` 宏等价于: > `{ WM_CREATE, 0, 0, 0, AfxSig_vv, (AFX_PMSG)&OnCreate }` > 其中 `AfxSig_vv` 表示函数签名为 `void func(void)` [^3][^4]. --- ### **
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值