初学MFC,还有很多内部机理并不太了解,还是自己给自己打气,将每天的心得记录下来.
消息的传递与发送是Windows应用程序的核心所在,任何事件的触发与响应均要通过消息的作用才能得以完成。在SDK编程中,对消息的获取与分发主要是通过消息循环来完成的,而在MFC编程中则是通过采取消息映射的方式对其进行处理的。相比而言,这样的处理方式要简单许多,这也是符合面向对象编程中尽可能隐含实现细节的原则。
一、消息的类别
1、窗口消息:即标准的WINDOWS消息,它与创建窗口,绘制窗口,移动窗口和销毁窗口等操作窗口的动
作有关,这类消息是以WM_为前缀,不过WM_COMMAND例外.
例如: WM_CREATE -- 创建窗口后立即发出的一个消息,用于指示窗口初始化
WM_CLOSE -- 通知窗口要将它关闭
WM_PAINT -- 通知窗口绘制自身
WM_LBUTTONDOWN -- 通知窗口在它的客户区中按下了鼠标左键
WM_MOVE、WM_QUIT等等.. 其他的可查看微软发布的技术参考资料 -- 窗口消息的类别。
2、命令消息:以WM_COMMAND为消息名.在消息中含有命令的标志符ID(wParam的低16位;而高16位
例如: WM_CREATE -- 创建窗口后立即发出的一个消息,用于指示窗口初始化
WM_CLOSE -- 通知窗口要将它关闭
WM_PAINT -- 通知窗口绘制自身
WM_LBUTTONDOWN -- 通知窗口在它的客户区中按下了鼠标左键
WM_MOVE、WM_QUIT等等.. 其他的可查看微软发布的技术参考资料 -- 窗口消息的类别。
2、命令消息:以WM_COMMAND为消息名.在消息中含有命令的标志符ID(wParam的低16位;而高16位
为0L),以区分具体的命令.由菜单,工具栏等命令接口对象产生.
注意:
凡是从基类CCmdTarget派生的类都能处理命令消息
命令消息的lParam为0L,区分于控件消息
3、控件消息:控件通知消息也是以WM_COMMAND为消息名.由编辑框,列表框,子窗口发送给父窗口的
注意:
凡是从基类CCmdTarget派生的类都能处理命令消息
命令消息的lParam为0L,区分于控件消息
3、控件消息:控件通知消息也是以WM_COMMAND为消息名.由编辑框,列表框,子窗口发送给父窗口的
通知消息.
目前控件消息有3种格式:
仿窗口消息格式 -- 如:WM_HSCROLL 或者 WM_VSCROLL : 滚动控件通知父窗口沿水平或者垂直
仿窗口消息格式 -- 如:WM_HSCROLL 或者 WM_VSCROLL : 滚动控件通知父窗口沿水平或者垂直
方向滚动窗口
WM_PARENTNOTIFY: 通知控件窗口建立或销毁其他事件
WM_CTLCOLOR : 通知父窗口要改变控件的颜色
WM_DRAWITEM,WM_DELETEITEM,WM_MEASUREITEM,WM_CHARTOITEM:
WM_PARENTNOTIFY: 通知控件窗口建立或销毁其他事件
WM_CTLCOLOR : 通知父窗口要改变控件的颜色
WM_DRAWITEM,WM_DELETEITEM,WM_MEASUREITEM,WM_CHARTOITEM:
通知父窗口将绘制控件自身窗口
仿命令消息格式 -- messge为WM_COMMAND;wParam底16位为控件ID,高16位为消息通知码;
仿命令消息格式 -- messge为WM_COMMAND;wParam底16位为控件ID,高16位为消息通知码;
lParam为控件窗口句柄
单独控件消息格式 -- message为WM_NOTIFY;wParam为控件ID;lParam为指向NMHDR的指针
NMHDR 定义如下:
typedef struct tagNMHDR{
HWND hwndFrom ; -- 发出控件消息的控件窗口的句柄
UINT idFrom ; -- 控件的资源ID
UINT code ; -- 控件消息的消息通知码EX_XXX
}NMHDR ;
二、命令的分解和组装:由于命令消息与控件消息的wParam的高16位和低16位分别表示不同的含义,
单独控件消息格式 -- message为WM_NOTIFY;wParam为控件ID;lParam为指向NMHDR的指针
NMHDR 定义如下:
typedef struct tagNMHDR{
HWND hwndFrom ; -- 发出控件消息的控件窗口的句柄
UINT idFrom ; -- 控件的资源ID
UINT code ; -- 控件消息的消息通知码EX_XXX
}NMHDR ;
二、命令的分解和组装:由于命令消息与控件消息的wParam的高16位和低16位分别表示不同的含义,
因此需要分解或组装工作
分解:
HIGHWORD(wParam) -- 获得wParam 的高16位
LOWWORD(wParam) -- 获得wParam 的低16位
组装:
命令消息组装 -- 如: (WM_COMMAND,MAKEWPARAM(ID_FILE_OPEN,0),0L)
控件消息组装 -- 如: (WM_COMMAND,MAKEWPARAM(IDOK,BN_CLICKED),hwnd)
HIGHWORD(wParam) -- 获得wParam 的高16位
LOWWORD(wParam) -- 获得wParam 的低16位
组装:
命令消息组装 -- 如: (WM_COMMAND,MAKEWPARAM(ID_FILE_OPEN,0),0L)
控件消息组装 -- 如: (WM_COMMAND,MAKEWPARAM(IDOK,BN_CLICKED),hwnd)
三、SendMessage和PostMessage函数
1、SendMessage和PostMessage函数只能给窗口发消息,也就是说只能给CWnd类及其派生类对象
对应的窗口发消息。
语法:
SendMessage(HWND hwnd,UINT message,WPARAM wParam ,LPARAM lParam ) -- 发送
int res = ::SendMessage(hwnd,message,wParam,lParam)
PostMessage(HWND hwnd,UINT message ,WPARAM wParam ,LPARAM lParam ) -- 寄送
int res = ::PostMessage(hwnd,message,wParam,lParam)
通常鼠标和键盘消息采用寄送方式,而其他消息采用发送方式
2、使用MFC发送或者寄送消息
int res = pWnd->SendMessage(UINT message,WPARAM wParam,LPARAM lParam) -- pWnd 为接收消息的目标窗口对象
int res = pWnd->PostMessage(UINT message,WPARAM wParam,LPARAM lParam) -- 同上
3、检索消息队列中的消息PeekMessage 和 GetMessage
语法:
BOOL res = ::PeekMessage(LPMSG lpMsg,HWND hwnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg)
BOOL res = ::GetMessage(LPMSG lpMsg ,HWND hwnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
lpMsg -- MSG结构变量的指针
hwnd -- 要截获消息的窗口
wMsgFilterMin,wMsgFilterMax -- 与lpMsg相对应,表示查看消息的范围,如果它们分别为0,0,则表示将查看所有的消息
wRemoveMsg -- 取值为 PM_REMOVE时,PeekMessage函数看完后,将删除消息.取值为 PM_NOREMOVE时,PeekMessage函数看完后,返回消息的一个备份,
不删除队列中的消息
功能:
PeekMessage -- 只是窥看一下消息队列,查询一下指定消息是否在在消息队列中;看完后,是否从队列中删除该消息,取决于标志wRemoveMsg
GetMessage -- 当没有一个新消息到达队列前,该函数一直阻塞,一旦有新消息时,则从队列中删除该消息,并返回此消息.
四、消息映射表
语法:
SendMessage(HWND hwnd,UINT message,WPARAM wParam ,LPARAM lParam ) -- 发送
int res = ::SendMessage(hwnd,message,wParam,lParam)
PostMessage(HWND hwnd,UINT message ,WPARAM wParam ,LPARAM lParam ) -- 寄送
int res = ::PostMessage(hwnd,message,wParam,lParam)
通常鼠标和键盘消息采用寄送方式,而其他消息采用发送方式
2、使用MFC发送或者寄送消息
int res = pWnd->SendMessage(UINT message,WPARAM wParam,LPARAM lParam) -- pWnd 为接收消息的目标窗口对象
int res = pWnd->PostMessage(UINT message,WPARAM wParam,LPARAM lParam) -- 同上
3、检索消息队列中的消息PeekMessage 和 GetMessage
语法:
BOOL res = ::PeekMessage(LPMSG lpMsg,HWND hwnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsg)
BOOL res = ::GetMessage(LPMSG lpMsg ,HWND hwnd,UINT wMsgFilterMin,UINT wMsgFilterMax)
lpMsg -- MSG结构变量的指针
hwnd -- 要截获消息的窗口
wMsgFilterMin,wMsgFilterMax -- 与lpMsg相对应,表示查看消息的范围,如果它们分别为0,0,则表示将查看所有的消息
wRemoveMsg -- 取值为 PM_REMOVE时,PeekMessage函数看完后,将删除消息.取值为 PM_NOREMOVE时,PeekMessage函数看完后,返回消息的一个备份,
不删除队列中的消息
功能:
PeekMessage -- 只是窥看一下消息队列,查询一下指定消息是否在在消息队列中;看完后,是否从队列中删除该消息,取决于标志wRemoveMsg
GetMessage -- 当没有一个新消息到达队列前,该函数一直阻塞,一旦有新消息时,则从队列中删除该消息,并返回此消息.
四、消息映射表
1、为了在类中加入静态消息映射表,MFC是通过一对宏来实现的
在类声明文件.h中的声明:
DECLARE_MESSAGE_MAP() -- 此句通常在类声明的最后
在类的实现文件.cpp中:
BEGIN_MESSAGE_MAP(类名,父类名)
………
消息映射入口项.
………
END_MESSAGE_MAP( )
2、命令消息映射入口项是一个ON_COMMAND的宏.比如文件菜单下的"打开…"菜单(ID值为ID_FILE_OPEN)对应的消息映射入口项为:
ON_COMMAND(ID_FILE_NEW,OnFileOpen)
3、在类定义中加入消息处理函数的函数原型(函数声明.h)
afx_msg OnFileOpen();// 函数原型
作为约定.消息处理函数一般以On打头
MFC还提供了其他一些用于消息映射的宏,详情可参见下表:
宏名 说明
DECLARE_MESSAGE_MAP -- 在头文件声明源文件中所含有的消息映射
BEGIN_MESSAGE_MAP -- 标记源文件消息映射的开始
END_MESSAGE_MAP -- 标记源文件消息映射的结束
ON_COMMAND -- 将特定命令的处理委派给类的一个成员函数
ON_CONTROL -- 映射一个函数到一个定制控制通知消息。其中,定制控制通知消息是从一个控制发送到其父窗口的消息。
ON_CONTROL_RANGE -- 将一个控制ID的范围映射到一个消息处理函数
ON_CONTROL_REFLECT -- 映射一个由父窗口反射回控制的通知消息
ON_MESSAGE -- 将一个用户自定义消息映射到一消息处理函数
ON_NOTIFY -- 映射一个控制消息到一个函数
ON_NOTIFY_RANGE -- 映射一个控制ID范围内的控制消息到一个函数
ON_NOTIFY_EX -- 映射一个控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应。
ON_NOTIFY_EX_RANGE -- 映射一个控制ID范围内的控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应
ON_NOTIFY_REFLECT -- 映射一个控制消息到一个函数。该消息将会被控制的父窗口反射回来。
ON_REGISTERED_MESSAGE -- 映射一个唯一的消息到一个将要处理该注册消息的函数上。该消息是由RegisterWindowMessage()函数注册的。
ON_UPDATE_COMMAND_UI -- 映射一个函数来处理一个用户接口更新命令消息
ON_UPDATE_COMMAND_UI_RANGE -- 映射一个命令ID的范围到一个更新消息处理函数
DECLARE_MESSAGE_MAP -- 在头文件声明源文件中所含有的消息映射
BEGIN_MESSAGE_MAP -- 标记源文件消息映射的开始
END_MESSAGE_MAP -- 标记源文件消息映射的结束
ON_COMMAND -- 将特定命令的处理委派给类的一个成员函数
ON_CONTROL -- 映射一个函数到一个定制控制通知消息。其中,定制控制通知消息是从一个控制发送到其父窗口的消息。
ON_CONTROL_RANGE -- 将一个控制ID的范围映射到一个消息处理函数
ON_CONTROL_REFLECT -- 映射一个由父窗口反射回控制的通知消息
ON_MESSAGE -- 将一个用户自定义消息映射到一消息处理函数
ON_NOTIFY -- 映射一个控制消息到一个函数
ON_NOTIFY_RANGE -- 映射一个控制ID范围内的控制消息到一个函数
ON_NOTIFY_EX -- 映射一个控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应。
ON_NOTIFY_EX_RANGE -- 映射一个控制ID范围内的控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应
ON_NOTIFY_REFLECT -- 映射一个控制消息到一个函数。该消息将会被控制的父窗口反射回来。
ON_REGISTERED_MESSAGE -- 映射一个唯一的消息到一个将要处理该注册消息的函数上。该消息是由RegisterWindowMessage()函数注册的。
ON_UPDATE_COMMAND_UI -- 映射一个函数来处理一个用户接口更新命令消息
ON_UPDATE_COMMAND_UI_RANGE -- 映射一个命令ID的范围到一个更新消息处理函数
五、MFC程序处理消息的路径
1、处理窗口消息
MFC对窗口消息的处理是最直观的,窗口消息只有窗口类能够接收和处理,在MFC应用程序框架的基本类中,只有框架类和视图是窗口类
当消息被BOOL CWnd::OnWndMsg(UINT message,WPARAM wParam,LPARAM lParam,LRESULT *pResult)函数判别为窗口消息时,OnWndMsg(...)将直接对消息所属的窗口类的消息
映射表进行搜索,找到匹配的消息处理函数后便执行它,否则继续搜索基类。若仍未找到则把消息交给默认处理函数LRESULT DefWindowProc(...)处理.
2、处理命令消息
理论上所有从基类CCmdTarget类派生的类均可处理命令消息,但是MFC应用程序框架对命令消息的处理作出了一个特别的顺序规定,它将依次检查MFC应用程序基本类的消息映射
表,以便判别各类是否定义了命令消息处理函数
命令传递路线:
命令--->视图类--->文档类--->框架窗口类--->应用程序类
3、处理控件消息
主框架窗口类的OnWndMsg(...)判别消息类型为WM_NOTIFY后,将调用该类的虚函数BOOL OnNotify(WPARAM wParam,LPARAM lParam,LRESULT &pResult).
MFC对窗口消息的处理是最直观的,窗口消息只有窗口类能够接收和处理,在MFC应用程序框架的基本类中,只有框架类和视图是窗口类
当消息被BOOL CWnd::OnWndMsg(UINT message,WPARAM wParam,LPARAM lParam,LRESULT *pResult)函数判别为窗口消息时,OnWndMsg(...)将直接对消息所属的窗口类的消息
映射表进行搜索,找到匹配的消息处理函数后便执行它,否则继续搜索基类。若仍未找到则把消息交给默认处理函数LRESULT DefWindowProc(...)处理.
2、处理命令消息
理论上所有从基类CCmdTarget类派生的类均可处理命令消息,但是MFC应用程序框架对命令消息的处理作出了一个特别的顺序规定,它将依次检查MFC应用程序基本类的消息映射
表,以便判别各类是否定义了命令消息处理函数
命令传递路线:
命令--->视图类--->文档类--->框架窗口类--->应用程序类
3、处理控件消息
主框架窗口类的OnWndMsg(...)判别消息类型为WM_NOTIFY后,将调用该类的虚函数BOOL OnNotify(WPARAM wParam,LPARAM lParam,LRESULT &pResult).
OnWndMsg(...)
|
|
v
OnNotify(...)
|
|
v
< 控件窗口类能 YES
处理该消息 > -----> 控件窗口类处理消息
| NO
v
控件的父窗口类::OnWndMsg(...)
六、自定义消息处理
|
|
v
OnNotify(...)
|
|
v
< 控件窗口类能 YES
处理该消息 > -----> 控件窗口类处理消息
| NO
v
控件的父窗口类::OnWndMsg(...)
六、自定义消息处理
1、自定义静态窗口消息
系统允许用户把自定义窗口消息映射为范围在WM_USER + 1 到 WM_USER + ox7fff
需做工作:
1) 在类的实现文件中定义消息
#define WM_SAYHELLO WM_USER+100
2) 在类的声明消息处理函数(假定为CMyView类)
class CMyView:public CView
{
...
protected:
...
afx_msg LRESULT OnSayHello(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
}
3) 在类的消息映射表中加入映射项
BEGIN_MESSAGE_MAP(CMyView,CView)
...
ON_MESSAGE(WM_SAYHELLO,OnSayHello)
...
END_MESSAGE_MAP()
系统允许用户把自定义窗口消息映射为范围在WM_USER + 1 到 WM_USER + ox7fff
需做工作:
1) 在类的实现文件中定义消息
#define WM_SAYHELLO WM_USER+100
2) 在类的声明消息处理函数(假定为CMyView类)
class CMyView:public CView
{
...
protected:
...
afx_msg LRESULT OnSayHello(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
}
3) 在类的消息映射表中加入映射项
BEGIN_MESSAGE_MAP(CMyView,CView)
...
ON_MESSAGE(WM_SAYHELLO,OnSayHello)
...
END_MESSAGE_MAP()
4) 在类的实现文件中实现消息处理函数
LRESULT CMyView::OnSayHello(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Hello.I am in CMyView");
}
5) 给窗口发送消息
pView->SendMessage(WM_SAYHELLO,0L,0L); 或 pView->PostMessage(WM_SAYHELLO,0L,0L);
2、自定义动态窗口消息
1) 定义并注册消息(.cpp)
#define MESSAGE_NAME "2005-07-24-THIS-IS-A-MESSAGE-TEST"
UINT WM_MYSAYHELLO = ::RegisterWndMessage(MESSAGE_NAME);
2) 在类中声明消息处理函数(假定为CMyView)
class CMyView:public CView
{
...
protected:
...
afx_msg LRESULT OnSayHello(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
}
3) 在类的消息映射表中加入映射项
ON_REGISTERED_MESSAGE(WM_MYSAYHELLO,OnSayHello)
4) 在类实现文件中实现消息处理函数
LRESULT CMyView::OnSayHello(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Hello,I am in CMyView.");
}
5) 给窗口发送消息
pView->SendMessage(WM_SAYHELLO,0L,0L); 或 pView->PostMessage(WM_SAYHELLO,0L,0L);
LRESULT CMyView::OnSayHello(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Hello.I am in CMyView");
}
5) 给窗口发送消息
pView->SendMessage(WM_SAYHELLO,0L,0L); 或 pView->PostMessage(WM_SAYHELLO,0L,0L);
2、自定义动态窗口消息
1) 定义并注册消息(.cpp)
#define MESSAGE_NAME "2005-07-24-THIS-IS-A-MESSAGE-TEST"
UINT WM_MYSAYHELLO = ::RegisterWndMessage(MESSAGE_NAME);
2) 在类中声明消息处理函数(假定为CMyView)
class CMyView:public CView
{
...
protected:
...
afx_msg LRESULT OnSayHello(WPARAM,LPARAM);
DECLARE_MESSAGE_MAP()
}
3) 在类的消息映射表中加入映射项
ON_REGISTERED_MESSAGE(WM_MYSAYHELLO,OnSayHello)
4) 在类实现文件中实现消息处理函数
LRESULT CMyView::OnSayHello(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Hello,I am in CMyView.");
}
5) 给窗口发送消息
pView->SendMessage(WM_SAYHELLO,0L,0L); 或 pView->PostMessage(WM_SAYHELLO,0L,0L);