MFC消息映射及自定义消息介绍

⌚Time: 2022-03-06 21:03:18

👨‍💻Author: Jack Ge

个人对MFC消息处理机制的理解,系统产生事件消息,触发程序的响应函数对事件进行处理。


MFC消息类型

在MFC中消息分为系统定义消息和程序定义消息。


三个重要的宏:

DECLARE_MASSAGE_MAP()

声明该类定义了一个消息映射。 程序中的每个 CCmdTarget 派生类都必须提供一个消息映射来处理消息。

在类声明的末尾使用 DECLARE_MESSAGE_MAP 宏。

如果你在DECLARE_MESSAGE_MAP之后定义了成员,那么你必须为它们指定新的访问类型(public,private 或protected)

BEGIN_MESSAGE_MAP(theClass, baseClass )

theClass:消息映射类的名称

baseClass:消息映射类的基类名称

END_MESSAGE_MAP()

在类定义成员函数的实现 (.cpp) 文件中,使用 BEGIN_MESSAGE_MAP 宏启动消息映射,然后为每个消息处理程序函数添加宏条目,并使用 END_MESSAGE_MAP 宏完成消息映射。


系统定义消息

系统定义消息有三种类型,分为标准消息(窗口消息),命令消息,通告消息(控件通知消息)。

标准消息(WM_xxx):

除WM_COMMAND之外,所有以WM_开头的消息。例如ON_WM_SYSCOMMAND(),ON_WM_PAINT()。从CWnd派生的类,都可以接收到这类消息。

我发现此类消息函数只有宏定义,没有与任何函数绑定,是因为,此类消息的处理函数名称固定,不可更改。标准的Windows消息,不需要指定处理函数的名称,每个标准Windows消息都有自己的默认处理函数名称。如WM_LBUTTONDOWN所对应的处理函数名称为OnLButtonDown。

WM_xxx 消息对应的宏为:ON_WM_xxx


BEGIN_MESSAGE_MAP(CaDlg, CDialog)

//标准消息宏

    ON_WM_SYSCOMMAND()

    ON_WM_PAINT()

    ...

END_MESSAGE_MAP()

在vs2005的类视图中,找到窗体类,右键点击,选择属性。在属性页中点击消息。即可看到窗口消息的所有类型,点击右侧单元格即可添加相应的消息处理函数

命令消息(WM_COMMAND):

来自菜单、加速键或工具栏按钮的消息。这类消息都以WM_COMMAND呈现。在MFC中,通过菜单项的标识(ID)来区分不同的命令消息;在SDK中,通过消息的wParam参数识别。从CCmdTarget派生的类,都可以接收到这类消息。

WM_COMMAND消息中有两个参 数,wparam、lparam,定义如下:

wParam 高位字 通知码

wParam 低位字 命令ID

lParam 发送命令消息的子窗体句柄。

wParam高位字:如果信息是从控件传来,参数wParam高位字指定通知代码。如果信息是从菜单中传来,这个值是零。如果信息是从一个快捷键传来,这个值是1 。

wParam低位字:菜单项,控件或快捷键的标识符。

lParam:如果信息是从控件传来,则 lParam是控件的句柄,如果是从菜单或者快捷键传来,则是0

WM_COMMAND消息 消息对应的宏为

ON_COMMAND(ID,pfn)

ON_COMMAND_EX(ID,pfn)


BEGIN_MESSAGE_MAP(CaDlg, CDialog)

...

//命令消息宏

ON_COMMAND(ID_1_2, &CaDlg::On12)

...

END_MESSAGE_MAP()

在vs资源视图中打开菜单,对菜单项右击,添加事件处理程序,添加相应的消息处理函数

通告消息(WM_NOTIFY):

随着控件的种类越来越多,越来越复杂(如列表控件、树控件等),仅仅将wParam,lParam将视为一个32位无符号整数,已经装不下太多信息了。为了给父窗口发送更多的信息,微软定义了一个新的WM_NOTIFY消息来扩展WM_COMMAND消息。

由控件产生的消息,例如,按钮的单击,列表框的选择等均产生此类消息,为的是向其父窗口(通常是对话框)通知事件的发生。这类消息也是以WM_COMMAND形式呈现。从CCmdTarget派生的类,都可以接收到这类消息。

WM_NOTIFY消息仍然使用MSG消息结构,只是此时wParam为控件ID,lParam为一个NMHDR指针,

WM_NOTIFY其实就是WM_COMMAND。实际中依然是使用WM_COMMAND来传送WM_NOTIFY。

如BN_CLICKED通知码为WM_COMMAND的wParam的高位字节。

通告消息的宏一般为在通知码前面加上 ON_


BEGIN_MESSAGE_MAP(CaDlg, CDialog)

...

//通告消息宏

ON_BN_CLICKED(IDCANCEL, &CaDlg::OnBnClickedCancel)

...

END_MESSAGE_MAP()

在vs的资源视图中打开窗体,对控件右击,添加事件处理程序,根据向导添加控件的响应函数

MFC消息映射的三处实现

MFC实现消息映射的三处信息,函数原型,函数实现,用来关联消息和消息响应的宏。

为菜单增加点击事件为例,命令消息的三处实现:

在类声明中,定义消息处理函数,并且使用afx_msg作为修饰,在类最后定义DECLARE_MASSAGE_MAP()宏


protected:

    afx_msg void On12();

    DECLARE_MESSAGE_MAP()

};

在cpp文件中实现函数


void CaDlg::On12()

{

    // TODO: 在此添加命令处理程序代码

    MessageBox(L"Menu clicked!");

}

cpp文件中定义宏进行消息映射


BEGIN_MESSAGE_MAP(CaDlg, CDialog)

...

//命令消息宏

ON_COMMAND(ID_1_2, &CaDlg::On12)

...

END_MESSAGE_MAP()

只要定义了上述与消息有关的三处信息后,即可实现消息的响应处理


程序定义消息

MFC允许用户自定义消息,实现自定义消息分为4步

1.定义消息

系统定义消息ID范围:0x0000-0x03FF,程序定义消息ID范围:0x0400-0x7FFF。系统定义的消息都是小于WM_USER的,所以我们定义消息的ID就要大于WM_USER。推荐大100以上


#define WM_MY_MESSAGE (WM_USER+100)

2.在类声明中写消息处理函数的声明


    afx_msg LRESULT on_my_message(WPARAM wParam,LPARAM lParam);

    ...

    DECLARE_MASSAGE_MAP()

3.在BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间添加消息映射宏

MFC自定义消息宏为:ON_MESSAGE(message, memberFxn)


BEGIN_MESSAGE_MAP(CaDlg, CDialog)

...

ON_MESSAGE(WM_MY_MESSAGE,CaDlg::on_my_message)

...

END_MESSAGE_MAP()

4.实现消息处理函数


LRESULT CaDlg::on_my_message(WPARAM wParam,LPARAM lParam){

    MessageBox(L"custom message triggered!");

    return 0;

}

自定义消息映射就完成了。

之后,在需要的地方,发射消息,触发消息映射


SendMessage(WM_MY_MESSAGE);