此说明介绍了 MFC 消息映射设施。
问题
Microsoft Windows 在使用其消息传送设施的窗口类中实现虚拟函数。 由于涉及大量消息,为每个 Windows 消息提供单独的虚拟功能将创建一个极其大型的 vtable。
由于系统定义的 Windows 消息数随时间而变化,并且应用程序可以定义自己的 Windows 消息,因此消息映射提供了一个间接级别,以防止接口更改中断现有代码。
概述
MFC 提供了在传统基于 Windows 的程序中使用的 switch 语句的替代方法,用于处理发送到窗口的消息。 可以定义从消息到方法的映射,以便在窗口收到消息时自动调用相应的方法。 此消息映射设施设计为类似于虚拟函数,但C++虚拟函数无法带来额外的优势。
定义消息映射
DECLARE_MESSAGE_MAP宏声明类的三个成员。
名为 _messageEntries的AFX_MSGMAP_ENTRY条目的私有数组。
一个名为 messageMap 的受保护AFX_MSGMAP结构,指向 _messageEntries 数组。
一个调用
GetMessageMap
的受保护虚拟函数,用于返回 messageMap 的地址。
应将此宏放入使用消息映射的任何类的声明中。 按照约定,它位于类声明的末尾。 例如:
class CMyWnd : public CMyParentWndClass
{
// my stuff...
protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
这是 AppWizard 和 ClassWizard 在创建新类时生成的格式。 ClassWizard 需要 //{{ 和 //}} 括号。
消息映射的表是使用一组扩展为消息映射条目的宏定义的。 表以 BEGIN_MESSAGE_MAP 宏调用开头,该调用定义此消息映射处理的类和传递未处理消息的父类。 该表以 END_MESSAGE_MAP 宏调用结尾。
在这两个宏调用之间,是此消息映射要处理的每个消息的条目。 每个标准 Windows 消息都有一个窗体的宏ON_WM_MESSAGE_NAME ,该宏生成该消息的条目。
已定义标准函数签名,用于解压缩每个 Windows 消息的参数并提供类型安全性。 可以在 CWnd 声明的 Afxwin.h 文件中找到这些签名。 每一个都用关键字 afx_msg 进行标记,以便轻松识别。
注释
ClassWizard 要求在消息映射处理程序声明中使用 afx_msg 关键字。
这些函数签名是通过使用简单的约定派生的。 函数的名称始终以“开头 "On
。 随后是 Windows 消息的名称,其中删除了“WM_”,每个单词的第一个字母大写。 参数的顺序为 wParam , LOWORD
后跟 (lParam), 然后 HIWORD
(lParam)。 未使用的参数不会传递。 MFC 类包装的任何句柄都转换为指向相应 MFC 对象的指针。 以下示例演示如何处理WM_PAINT消息并导致调用函数 CMyWnd::OnPaint
:
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
必须在任何函数或类定义范围之外定义消息映射表。 不应将其置于外部的“C”块中。
注释
ClassWizard 将修改 //{{ 和 //}} 注释括号之间发生的消息映射条目。
用户定义的 Windows 消息
用户定义的消息可以使用 ON_MESSAGE 宏包含在消息映射中。 此宏接受消息号和窗体的方法:
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
在此示例中,我们为自定义消息建立一个处理程序,该消息的 Windows 消息 ID 派生自用户定义的消息的标准WM_USER基。 以下示例演示如何调用此处理程序:
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
使用此方法的用户定义消息的范围必须位于WM_USER范围内才能0x7fff。
注释
ClassWizard 不支持从 ClassWizard 用户界面输入ON_MESSAGE处理程序例程。 必须从 Visual C++ 编辑器手动输入它们。 ClassWizard 将分析这些条目,并让你像任何其他消息映射条目一样浏览它们。
已注册的 Windows 消息
RegisterWindowMessage 函数用于定义保证在整个系统中唯一的新窗口消息。 宏ON_REGISTERED_MESSAGE用于处理这些消息。 此宏接受包含已注册的 Windows 消息 ID 的 UINT NEAR 变量的名称。 例如:
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
注册的 Windows 消息 ID 变量(在此示例中WM_FIND)必须是 NEAR 变量,因为实现ON_REGISTERED_MESSAGE的方式。
使用此方法的用户定义消息的范围将位于0xC000 0xFFFF范围内。
注释
ClassWizard 不支持从 ClassWizard 用户界面输入ON_REGISTERED_MESSAGE处理程序例程。 必须从文本编辑器手动输入它们。 ClassWizard 将分析这些条目,并让你像任何其他消息映射条目一样浏览它们。
命令消息
使用ON_COMMAND宏在消息映射中处理来自菜单和加速器的命令消息。 此宏接受命令 ID 和方法。 只有 wParam 等于指定命令 ID 的特定WM_COMMAND消息由消息映射条目中指定的方法处理。 命令处理程序成员函数不采用任何参数并返回 void
。 该宏采用以下形式:
ON_COMMAND(id, memberFxn)
命令更新消息通过同一机制路由,但改用ON_UPDATE_COMMAND_UI宏。 命令更新处理程序成员函数采用单个参数、指向 CCmdUI 对象的指针并返回 void
。 宏具有窗体
ON_UPDATE_COMMAND_UI(id, memberFxn)
高级用户可以使用ON_COMMAND_EX宏,这是命令消息处理程序的扩展形式。 该宏提供ON_COMMAND功能的超集。 扩展的命令处理程序成员函数采用单个参数、包含命令 ID 的 UINT 并返回 BOOL。 返回值应为 TRUE ,以指示已处理命令。 否则,路由将继续路由到其他命令目标对象。
这些表单的示例:
内部 Resource.h (通常由 Visual C++ 生成)
#define ID_MYCMD 100 #define ID_COMPLEX 101
类声明内
afx_msg void OnMyCommand(); afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI); afx_msg BOOL OnComplexCommand(UINT nID);
消息映射定义内部
ON_COMMAND(ID_MYCMD, OnMyCommand) ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand) ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
在实现文件中
void CMyClass::OnMyCommand() { // handle the command } void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI) { // set the UI state with pCmdUI } BOOL CMyClass::OnComplexCommand(UINT nID) { // handle the command return TRUE; }
高级用户可以使用单个命令处理程序处理一系列命令: ON_COMMAND_RANGE 或ON_COMMAND_RANGE_EX。 有关这些宏的详细信息,请参阅产品文档。
注释
ClassWizard 支持创建ON_COMMAND和ON_UPDATE_COMMAND_UI处理程序,但它不支持创建ON_COMMAND_EX或ON_COMMAND_RANGE处理程序。 但是,类向导将分析和浏览所有四个命令处理程序变体。
控制通知消息
从子控件发送到窗口的消息在其消息映射条目中具有额外的信息位:控件的 ID。 仅当以下条件为 true 时,才会调用消息映射条目中指定的消息处理程序:
控件通知代码( lParam 的高字),如BN_CLICKED,与消息映射条目中指定的通知代码匹配。
控件 ID (wParam) 与消息映射条目中指定的控件 ID 匹配。
自定义控件通知消息可以使用 ON_CONTROL 宏通过自定义通知代码定义消息映射条目。 此宏具有窗体
ON_CONTROL(wNotificationCode, id, memberFxn)
对于高级用法 ON_CONTROL_RANGE 可用于处理来自具有相同处理程序的一系列控件的特定控件通知。
注释
ClassWizard 不支持在用户界面中创建ON_CONTROL或ON_CONTROL_RANGE处理程序。 必须使用文本编辑器手动输入它们。 ClassWizard 将分析这些条目,并让你像任何其他消息映射条目一样浏览它们。
Windows 通用控件对复杂控件通知使用更强大的 WM_NOTIFY 。 此版本的 MFC 使用ON_NOTIFY和ON_NOTIFY_RANGE宏直接支持此新消息。 有关这些宏的详细信息,请参阅产品文档。