一、Windows消息的分类
1、标准消息:除WM_COMMAND外,其他所有以WM开头的消息;
2、非标准消息:以WM_COMMAND形式呈现:
命令消息:由菜单、工具栏、加速键产生,在MFC中通过菜单项的标识(ID)区分不同消息,在SDK中通过消息的wParam参数来区别不同消息,从CCmdTarget类派生下来的类,都可以接收这类消息。
通告消息:有控件产生,比如:按钮的单击、列表框的选择都会产生此类消息,目的是通告事件的发生,这类消息的目的是为了向父窗口(通常是对话框)通知事件的发生。比如:按钮的单击、列表框的选择都会产生这类消息,目的是通告事件的发生,这类消息目的是为了向父窗口(通常是对话框)通知事件的发生,这类消息也是以WM_COMMAND
形式呈现的,从CCmdTarget派生的类,都可以收到该消息
二、消息绕行
1、标准消息
标准windows消息根据MessageMap索引,从派生类流向基类,没有旁流的可能。
2、非标准消息
如果命令消息时WM_COMMAND,则消息的流向较为特殊。
(1)、与消息绕行有关的成员函数
AfxWndProc
AfxCallWndProc
virtual CCmdTarget::OnCmdMsg
virtual CDocument::OnCmdMag
virtual CWnd::WindowProc
virtual CWnd::OnCommand
virtual CWnd::DefWindowProc
virtual CFrameWnd::OnCommand
virtual CFrameWnd::OnCmdMsg
virtual CView::OnCmdMsg
(2)、消息绕行过程
Windows应用程序的动力源在CWinThread::Run中,该线程函数维持消息泵的运行。
*首先在WinThread::Run函数中不断调用API函数AfxWndProc;
*接着是AfxCallWndProc,在该函数中调用pWnd->WindowProc;
*pWnd->WindowProc是虚函数,如果是pWnd是从CFrameWnd派生下来的,那么调用的就是CFrameWnd::WindowProc,如果pWnd是从CView派生下来的,那么调用的就是CView::WindowProc, 由于CView和CFrameWnd都没有改写WindowProc,因此调用的都是CWnd::WindowProc;
*CWnd::WindowProc函数首先判断消息是否是WM_COMMAND,如果不是使用消息映射表比对AFX_MSGMAP_ENTRY中的每一个元素,比对成功调用相应处理函数;如果是WM_COMMAND消息,调用虚函数OnCommand;如果使pWnd是CFrameWnd的派生类,调用CFrameWnd::OnCommand,CFrameWnd::OnCommand执行末尾会返回CWnd::OnCommand的执行结果,如果使CView的派生类,应该调用CView的OnCommand函数,因为CView没有改写OnCommand函数,因此实际调用CWnd::OnCommand.
*CWnd::OnCommand返回虚函数OnCmdMsg的结果。OnCmdMsg是CCmdTarget的虚函数:
根据this指针的指向,可能调用的函数是CFrameWnd::OnCmdMsg或CView::OnCmdMsg或CDocument::OnCmdMsg或CWinApp::OnCmdMsg(由于CWinApp未改写该函数,所以调用的是CCmdTarget::OnCmdMsg)
--->如果调用的是CFrameWnd::OnCmdMsg,则该函数按照以下顺序执行:
CView* pView = GetActiveView();
if (pView->OnCmdMsg(nID, nCode))
return TRUE;
// then pump through frame
if (CWnd::OnCmdMsg(nID, nCode))
return TRUE;
// last but not least, pump through app
CWinApp* pApp = AfxGetApp();
if (pApp->OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
--->如果调用的是CView::OnCmdMsg,则该函数按照以下顺序执行:
BOOL CView::OnCmdMsg(UINT nID, int nCode)
{
cout<< "CView::OnCmdMsg()" << endl;
if(CWnd::OnCmdMsg(nID, nCode))
return TRUE;
BOOLbHandled = FALSE;
bHandled = m_pDocument->OnCmdMsg(nID, nCode);
return bHandled;
}
--->如果调用的是CWinApp::OnCmdMsg或CWnd::OnCmdMsg,则由于CWinApp和CWnd没有改写该函数,所以会调用CCmdTarget::OnCmdMsg)函数,该函数按照以下顺序执行:
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode)
{
cout << "CCmdTarget::OnCmdMsg()" << endl;
// Now look through message map to see if it applies to us
AFX_MSGMAP* pMessageMap;
AFX_MSGMAP_ENTRY* lpEntry;
for(pMessageMap = GetMessageMap(); pMessageMap != NULL;
pMessageMap= pMessageMap->pBaseMessageMap)
{
lpEntry= pMessageMap->lpEntries;
}
}
--->如果调用的是CDocument::OnCmdMsg,则该函数按照以下顺序执行:
BOOL CDocument::OnCmdMsg(UINT nID, int nCode)
{
if (CCmdTarget::OnCmdMsg(nID, nCode))
return TRUE;
return FALSE;
}
*如果此时仍未找到匹配的消息处理函数,则退回到CWnd::WindowProc,然后调用CWnd::DefWindowProc,该函数最终会调用WindowsAPI中的DefWindowsProc。消息绕行到此结束。
(3)、当CMyFrameWnd 对象获得一个WM_COMMAND,所引起的函数调用次序。