Windows编程基础–第19节 MFC之自定义消息

windows程序中最重要的就是消息机制,前面几节我们都是使用Windows系统设定好的消息进行编程,今天我们来使用自定义的消息来看看MFC中消息的传递过程;
MFC中自定义消息有两种方法:

  1. #define WM_MY_DEFINED_MSG (WM_USR+100)
  2. RegisterWindowMessage(WM_MY_REGISTER_MSG_SRR)
    下面我们来尝试一下这两种方法有什么不同;

1. 新建项目

新建一个基于对话框项目“day21”,布局如下,自行处理:

这里写图片描述
这里写图片描述

2.第一种自定义消息

2.1 定义消息ID

我们来到day20Dlg.h 的头文件中,在上方创建一个宏定义如下:

1
#define WM_MY_FRIST_MSG (WM_USER + 100)

这里我们定义了一个WM_MY_FRIST_MSG整型数值,它的值时WM_USER + 100,其中WM_USER 是微软定义好的宏,它的值是:#define WM_USER 0x0400 ,这说明在0x0400之前的消息(包含0x0400)都已经被windows系统使用,我们自定义消息只能在这之后,所以我们加上了100;

2.2 映射消息函数

我们切换到day20Dlg.cpp中,找到下面这地方:

1
2
3
4
5
BEGIN_MESSAGE_MAP(Cday21Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()

这就是消息的关系图,我们来添加一条映射,如下:

1
2
3
4
5
6
BEGIN_MESSAGE_MAP(Cday21Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_MY_FRIST_MSG, &Cday21Dlg::OnMyFirstMsgTrigger)
END_MESSAGE_MAP()

ON_MESSAGE(WM_MY_FRIST_MSG, &Cday21Dlg::OnMyFirstMsgTrigger)
说明将WM_MY_FRIST_MSG消息ID与类Cday21Dlg的OnMyFirstMsgTrigger成员函数对应,当WM_MY_FRIST_MSG消息被触发时将调用OnMyFirstMsgTrigger方法;

2.3 实现消息函数

我们先切换到Cday21Dlg类中,添加OnMyFirstMsgTrigger方法的声明:

afx_msg LRESULT OnMyFirstMsgTrigger(WPARAM wParam, LPARAM lParam);
这是消息映射函数的固定格式,参数类型也是固定的;

接下来我们实现OnMyFirstMsgTrigger的定义,如下:

1
2
3
4
5
6
7
LRESULT Cday21Dlg::OnMyFirstMsgTrigger(WPARAM wParam,  LPARAM lParam)
{
TCHAR szBuf[128];
_stprintf(szBuf,_T("消息:%s 被触发,消息ID: 0x%x "),_T("WM_MY_FRIST_MSG"),WM_MY_FRIST_MSG);
AfxMessageBox(szBuf);
return 0;
}

代码解释:

  1. 只要执行这个函数就会弹窗显示消息名称和ID;

2.4 发送消息

我们双击资源界面上的“自定义消息ID”按钮,编辑其点击事件处理函数:

1
2
3
4
5
6
void Cday21Dlg::OnBnClickedBtnDefined()
{
// TODO: 在此添加控件通知处理程序代码

PostMessage(WM_MY_FRIST_MSG,0,0);
}

代码解释:

  1. 使用PostMessage发送WM_MY_FRIST_MSG消息;

2.5 运行结果

这里写图片描述
这里写图片描述

3.第二种自定义消息

3.1 定义消息ID

由于我们需要使用RegisterWindowMessage(LPCSTR lpString)来创造消息ID,所以我们需要先定义一个字符串:

1
#define WM_MY_REGISTER_MSG_SRR _T("WM_MY_REGISTER_MSG_SRR")

接下来我们定义一个全局变量WM_MY_REGISTER_MSG来作为消息ID;

1
UINT WM_MY_REGISTER_MSG;

接下来我们来初始化消息ID,我们到Cday21Dlg类的构造函数中来创建消息ID:

1
2
3
4
5
6
Cday21Dlg::Cday21Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(Cday21Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
WM_MY_REGISTER_MSG = RegisterWindowMessage(WM_MY_REGISTER_MSG_SRR);
}

这样我们就通过RegisterWindowMessage来创建了消息ID,但只有在程序运行时我们才会知道消息ID是多少,并且每次运行时都可能不同;

3.2 映射消息函数

我们切换到day20Dlg.cpp中,我们再来添加一条映射,如下:

1
2
3
4
5
6
7
8
9
BEGIN_MESSAGE_MAP(Cday21Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_DEFINED, &Cday21Dlg::OnBnClickedBtnDefined)
ON_MESSAGE(WM_MY_FRIST_MSG, &Cday21Dlg::OnMyFirstMsgTrigger)
ON_REGISTERED_MESSAGE(WM_MY_REGISTER_MSG, &Cday21Dlg::OnMyRegisterMsgTrigger)
ON_BN_CLICKED(IDC_BTN_REG, &Cday21Dlg::OnBnClickedBtnReg)
END_MESSAGE_MAP()

ON_REGISTERED_MESSAGE(WM_MY_REGISTER_MSG, &Cday21Dlg::OnMyRegisterMsgTrigger)
说明将WM_MY_REGISTER_MSG消息ID与类Cday21Dlg的OnMyRegisterMsgTrigger成员函数对应,当WM_MY_REGISTER_MSG消息被触发时将调用OnMyRegisterMsgTrigger方法;
注意:RegisterWindowMessage方法获取的消息需要用ON_REGISTERED_MESSAGE映射消息

3.3 实现消息函数

我们先切换到Cday21Dlg类中,添加OnMyRegisterMsgTrigger方法的声明:

afx_msg LRESULT OnMyRegisterMsgTrigger(WPARAM wParam, LPARAM lParam);
这是消息映射函数的固定格式,参数类型也是固定的;

接下来我们实现OnMyRegisterMsgTrigger的定义,如下:

1
2
3
4
5
6
7
LRESULT Cday21Dlg::OnMyRegisterMsgTrigger(WPARAM wParam,  LPARAM lParam)
{
TCHAR szBuf[128];
_stprintf(szBuf,_T("消息:%s 被触发,消息ID: 0x%x "),_T("WM_MY_REGISTER_MSG"),WM_MY_REGISTER_MSG);
AfxMessageBox(szBuf);
return 0;
}

代码解释:

  1. 只要执行这个函数就会弹窗显示消息名称和ID;

2.4 发送消息

我们双击资源界面上的“REGISTER消息ID”按钮,编辑其点击事件处理函数:

1
2
3
4
void Cday21Dlg::OnBnClickedBtnReg()
{
PostMessage(WM_MY_REGISTER_MSG,0,0);
}

代码解释:

  1. 使用PostMessage发送WM_MY_REGISTER_MSG消息;

2.5 运行结果

这里写图片描述
这里写图片描述

4.对比两种方法

从上面两种方法我们可以看出其优缺点:

  1. #define WM_MY_DEFINED_MSG (WM_USR+100)
    定义的消息ID唯一,但不确定消息ID是否被其它地方占用,可能造成隐患;
  2. RegisterWindowMessage(WM_MY_REGISTER_MSG_SRR)
    优点:定义的消息ID唯一,独立;缺点:在程序运行前不能确定消息ID,不方便在其它程序中指定触发此消息ID;

项目源码可以访问我的码云

>>>我的私人博客<<<