任何事件的触发与响应均要通过消息的作用才能得以完成。在SDK编程中,对消息的获取与分发主要是通过消息循环来完成的,而在MFC编程中则是通过采取消息映射的方式对其进行处理的。
1、自定义消息及映射其流程
定义一个消息:一般在StdAfx.h 中声明一个消息.自定义消息一般从(VM_USER+100)开始,以免与系统定义的消息冲突:#define WM_MYMESSAGE (WM_USER+100)
在类的定义(XX.h)中声明消息响应函数:afx_msg LRESULT mymessagefunction(WPARAM wParam, LPARAM lParam);
添加消息映射,也就是将消息和消息要消息的函数之间的映射关系对应起来。
在XX.cpp中用ON_MESSAGE宏指令将消息映射到消息响应函数中.
BEGIN_MESSAGE_MAP(rightview, CView)
//{{AFX_MSG_MAP(rightview)
ON_MESSAGE(WM_MYMESSAGE,mymessagefunction)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
在XX.CPP中实现消息响应函数即可。
XX::mymessagefunction(WPARAM wParam, LPARAM lParam)
{
...............
}
一般POST方式用B00L PostMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam);
发送方式用LRESULT SendMessage(
HWND hWnd, // 目标窗口句柄
UINT Msg, // 被发送的消息
WPARAM wParam, // 第一个消息参数
LPARAM lParam // 第二个消息参数
);
两种方式参数一样;PostMessage()不等消息处理完毕就返回,SendMessage()必须等消息处理完毕才返回。
2、消息映射的工作原理
前面给出了消息映射的一般形式,下面就对消息映射的工作原理做更深入的分析。
任何使用了MFC应用程序框架的Windows程序都含有一个从 CWinApp派生的应用程序类对象,成员函数Run()将被隐含调用,其调用的CWinThread类成员函数 Run()将通过对GetMessage()、TranslateMessage()和DispatchMessage()等函数的调用完成同 WinMain()类似的消息循环。在消息处理中,几乎所有的窗口对象都使用AfxWndProc()窗口处理函数,并通过一个包含了窗口句柄和对象指针等信息的列表而获取到一个指向对象的指针,由此可以调用CWnd的虚函数WindowProc()。WindowProc()函数调用了CWnd的另一个成员函数OnWndMsg(),该函数首先检查到达的究竟是消息,命令还是通知(Notify),如果是消息就通过消息映射宏 DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP和END_MESSAGE_MAP 完成对消息的映射。在宏定义中封装了部分代码,这些被封装的预定义代码可以在VC安装目录下的"/MFC/Include/Afxwin.h"中找到,在编译时将为编译器所展开。
搜寻过程是从CMainWindow的消息入口开始的,DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP和 END_MESSAGE_MAP等消息映射宏通过搜索派生类消息映射的函数允许访问积累消息映射的入口。如果由CFrameWnd类派生的类 CMainWindow没有捕获通常由CFrameWnd捕获的消息,那么消息将由相同的由派生类所继承的CFrameWnd类函数捕获。同样,如果 CFrameWnd类仍没有捕获通常由其父类CWnd捕获的消息,则将继续上溯下去。这种消息映射的继承性与C++的继承是一致的。
另外,消息映射函数入口可以在在消息到达时为那些被隐含消息循环所调用的函数从中查看,并决定哪一个对象以及对象中的哪一个成员函数应该负责此消息的处理。虽然消息映射的内部工作原理比较复杂,但MFC通过预定义宏等手段将其完整的封装了起来,展现给开发人员的只是简单明了的MFC消息映射。
3、命令和通知
命令和通知实际都是一种特殊的消息类型。
在SDK编程中,菜单和控件的动作均会产生一个WM_COMMAND命令消息,通过对消息参数wParam的区分可以识别出具体是哪个控件或菜单发出的命令。
在MFC应用程序框架下,菜单和控件产生的消息将有所区分,选取菜单产生的消息被称作命令,而点击控件所产生的消息则被称作通知。由于命令和通知的本质仍是一种消息,因此在基本原理上仍是同消息一致的,即也是通过消息循环进入OnWndMsg()进而为对应的处理函数所响应。但是在使用上,命令和消息还是有区别的,其中一个最主要的区别是消息只有CWnd类的派生类所接收,而命令和通知则可以为所有从 CCmdTarget派生出去的类对象所接收,从MFC类的继承关系可以看出,除CWnd外CWinThread、CDocument和CDocItem 等也都可以接收命令和通知。除此之外,命令和通知在从消息循环进入到 OnWndMsg()后的这段过程也是同消息传递略有出入的。
这里CWnd::OnCommand()在检查完各项细节后、CWnd::OnNotify()在检查完不同条件后都调用了虚函数 OnCmdMsg()。这样,对于不同的菜单项和控件就可以有不同的实现。从下面给出的命令传递过程示例代码可以看出命令/通知的传递与消息的映射是非常类似的:
// 头文件.h文件//{{AFX_MSG(CDIP_SystemView) afx_msg void OnEmboss();
afx_msg void OnUpdateEmboss(CCmdUI* pCmdUI);//}}
AFX_MSGDECLARE_MESSAGE_MAP()……// 源文件.cpp文件
BEGIN_MESSAGE_MAP(CDIP_SystemView, CScrollView)/{ {AFX_MSG_MAP(CDIP_SystemView)
ON_COMMAND(IDM_EMBOSS, OnEmboss)
ON_UPDATE_COMMAND_UI(IDM_EMBOSS, OnUpdateEmboss)//}}AFX_MSG_MAP
END_MESSAGE_MAP()……
void CDIP_SystemView::OnEmboss (){ return;}……void CDIP_SystemView::OnUpdateStartPos(CCmdUI* pCmdUI){ pCmdUI->Enable(m_bCanUse);}这里ON_COMMAND宏将特定命令的处理同一个类成员函数建立了关联。而宏ON_UPDATE_COMMAND_UI则负责对命令的更新,即通过 CCmdUI对象控制菜单/控件的是否可用或其他一些状态变化的更新。对命令的更新也可以将其理解为存在一个含有每个菜单入口的大表,各菜单入口含有菜单是否可用的标志。在显示菜单时通过快速检查该表而做出其所对应的每一个菜单项是否可用的决定。如果可用标志发生了变化,该表也将得到及时的更新。