接触MFC有半年时间了,对于消息这一块一直不是很懂,中间有编过一些小程序也用到了消息,也只是把别人的粘贴过来,没有细研究。但是,对于MFC来讲这一部分无疑是很重要的一部分,是必须要搞懂的。今天花了一下午的时间将研究了一下消息,现在把它记下来,方便自己以后学习,也希望能给同样初学者一些帮助。
首先说一下消息的工作机制:
经常听到的一句话就是window是通过消息机制进行驱动的。那这个是什么意思了?打个比方,新建一个基于对话框的MFC程序,添加一个按钮,添加消息响应函数。我们都知道,当按下这个按钮的时候就会执行这个消息响应函数的内容。那为什么按下这个按钮就会执行这个函数了而不是其他的函数了?它是怎么知道是对应的按钮,而且是被按下了,我们先来看一下消息的结构。
消息的形式是这样定义的:
typedef structtagMSG { // msg
HWND hwnd; //窗口句柄
UINT message; //消息常量标识符
WPARAM wParam; //32位消息的特定附加信息,具体表示什么处决于message
LPARAM lParam; //32位消息的特定附加信息,具体表示什么处决于message
DWORD time; //消息创建时的时间
POINT pt; //消息创建时的鼠标位置
} MSG;
hwnd接收消息的32位窗口句柄。窗口可以是任何类型的屏幕对象,
因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑框等)。
message 用于区别其他消息的常量值,这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。
wParam 通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。
lParam 通常是一个指向内存中数据的指针。
现在我们知道消息的结构是什么样子了,为了全面的了解消息。我们来定义一个消息。
-
在头文件中注册消息:
static UINT WM_MY_MESSAGE =RegisterWindowMessage("Message");
----定义了一个名称为WM_MY_MESSAGE的消息。
-
在头文件中定义消息映射函数:
protected:
//{{AFX_MSG(CMessageTestView)
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAMlParam); //此行为添加代码
----定义了一个消息响应函数为OnMyMessage,那么后面两个参数是什么意思了,我们后面解释。
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
-
在其MessageTestView.cpp中,
先要声明响应消息:
BEGIN_MESSAGE_MAP(CMessageTestView, CEditView)
//{{AFX_MSG_MAP(CMessageTestView)
ON_REGISTERED_MESSAGE(WM_MY_MESSAGE,OnMyMessage)//此行添加代码定义唯一消息---将消息和消息响应函数关联起来
//}}AFX_MSG_MAP
-
再添加消息响应的函数实现:
LRESULT CMessageTestView::OnMyMessage(WPARAM wParam, LPARAM lParam)
{ return 0;}
至此,我们就自定义了一个完整的消息。剩下就是怎么触发这个消息了。触发消息分为两种:
第一种,用SendMessage或者PostMessage函数来实现。
PostMessage(unit Message, WPARAM wParam, LPARAM lParam)函数需要提供消息的名称,还有那个参数默认值为0,在消息响应函数处,我们就能获取到这两个参数。这种用法常用于类之间、进程之间的通信。
第二种,消息响应函数,响应窗口操作。这个就用一个经典的托盘实例来说一下。这是托盘的响应函数,从中我们可以看出wparam参数一般定义了控件的值,而lParam参数定义了操作的内容。
LRESULTCSMMSDlg::OnShowTask(WPARAM wParam, LPARAM lParam)
{
if(wParam !=IDR_MAINFRAME)//当我们点击托盘图标的时候,wparam参数将它的值传过来
return 1;
switch(lParam)//lParam参数,定义了操作的内容
{
caseWM_RBUTTONUP: //右键起来时弹出菜单
{
LPPOINT lpoint = new tagPOINT;
::GetCursorPos(lpoint);
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenu(MF_STRING,WM_DESTROY, "退ª?出?");
//menu.AppendMenu(MF_STRING,WM_DESTROY, "显?示º?");
menu.TrackPopupMenu(TPM_LEFTALIGN,lpoint->x ,lpoint->y, this);
HMENU hmenu = menu.Detach();
menu.DestroyMenu();
deletelpoint;
}
break;
caseWM_LBUTTONDBLCLK: //左键双击
{
this->ShowWindow(SW_SHOWNORMAL);
}
break;
}
return 0;
}
回到刚才那个按钮消息响应函数,我们发现它并没有任何参数,这为什么了?实际上所有的消息响应都有WPARAM和LPARAM的存在,只是有些消息响应WPARAM和LPARAM没有意义,所以在MFC封装后有些固定的消息响应函数看不到WPARAM和LPARAM,但依然可以通过GetCurrentMessage()取得当前的消息来查看WPARAM和LPARAM。
下面给出获取的代码:
const MSG*pMsg=GetCurrentMessage();
WPARAMwParam=pMsg->wParam;
LPARAMlParam=pMsg->lParam;
UINTmessage=pMsg->message;
if(wParam==IDC_BUTTON1)
{
AfxMessageBox("aa");
}
switch(lParam)
{
caseWM_LBUTTONDOWN:
{
AfxMessageBox("WM_LBUTTONDBLCLK");
}
break;
}
大家,如果有兴趣也可以看一下自动生成的系统消息,它的结构其实也满足上述规则。