TN006:消息映射

此说明介绍了 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_”,每个单词的第一个字母大写。 参数的顺序为 wParamLOWORD后跟 (lParam), 然后 HIWORDlParam)。 未使用的参数不会传递。 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宏直接支持此新消息。 有关这些宏的详细信息,请参阅产品文档。

另请参阅

按编号列出的技术说明
按类别列出的技术说明