深入浅出MFC学习笔记2--消息映射和消息路由

来源:互联网 发布:一对一聊天的软件 编辑:程序博客网 时间:2024/04/30 00:12

1.消息映射【消息的简单往上传递】

struct AFX_MSGMAP{    AFX_MSGMAP* pBaseMessageMap;    AFX_MSGMAP_ENTRY* lpEntries;};struct AFX_MSGMAP_ENTRY{    UINT nMessage;    UINT nCode;    UINT nID;    UINT nLastID;    UINT nSig;    AFX_PMSG pfn;};typedef void (CCmdTarget::*AFX_PMSG)(void);#define DECLARE_MESSAGE_MAP()\    static AFX_MSGMAP_ENTRY _messageEntries[];\    static AFX_MSGMAP messageMap;\    virtual AFX_MSGMAP* GetMessageMap() const;#define BEGIN_MESSAGE_MAP(theClass, baseClass)\    AFX_MSGMAP*theClass::GetMessageMap() const\    {\        return &theClass::messageMap;\    }\    AFX_MSGMAP theClass::messageMap = \    {\        // 对数组名取地址得到的仍然是一个地址,只不过此时此地址的类型不是默认的指向数组元素类型的指针【AFX_MSGMAP_ENTRY*】,而是指向数组的指针        //【AFX_MSGMAP_ENTRY(*)[]】        &baseClass::messageMap, (AFX_MSGMAP_ENTRY*)&(theClass::_messageEntries)    };\    AFX_MSGMAP_ENTRYtheClass::_messageEntries[] = \    {#define ON_COMMAN(id, memberFxn)\        {\            // 表面ON_COMMAND的处理函数只能是void (*)(void)类型            WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn        },\#define END_MESSAGE_MAP()\        {\            0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0\        }\    };

AfxSig_xx用来描述消息处理程序menberFxn的类型【参数和返回值】

消息映射的最原始类是CCmdTarget,对此类特殊处理:

AFX_MSGMAP CCmdTarget::messageMap = {    NULL, &CCmdTarget::_messageEntries[0]};AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] = {    { 0, 0, CCmdTargetid, 0, AfxSig_end, 0}};

// 1.由于此函数是CCmdTarget的虚函数,意味着你即使对一个没有采用消息映射宏的类对象用GetMessageMap,只要此类的基类有用的,你可以得到此基类的messageMap
// 2.只能对派生自CCmdTarget的类用消息映射宏,即使类的直接基类没有用消息映射宏,在本类的BEGIN_MASSAGE_MAP(theClass,baseClass)指定基类也是没问题的。
// 3.原因就是1行分析的

AFX_MSGMAP*  CCmdTarget::GetMessageMap() const{    return &CCmdTarget::messageMap;}

Frame7:
CCmdTarget, CWinApp, CDocument, CWnd, CFrameWnd, CView, CMyWinApp, CMyFrameWnd, CMyDoc, CMyView分别用DECLARE_MESSAGE_MAP
// 【CCmdTarget没用下面的,使用特殊处理的】

BEGIN_MESSAGE_MAP(theClass, baseClass)    ON_COMMAND(theClassid, 0)END_MESSAGE_MAP()

则会在程序开始运行之前就建立起一个消息传递网。从每一个应用上述宏的类获取其messageMap,可根据指向基类的指针依次往上找,最终都找到CCmdTarget的messageMap里。

2.命令传递【命令消息的传递过程】
上面把整个消息传递网架设起来了,消息进来时,会有一个泵推动它前进。消息如何进来,以及泵函数如何推动都属于windows程序设计的范涛。

上面的消息网中消息只能从子类流向父类,但有些消息应该有横向流动的机会。
MFC对消息循环的规定:
1.如果是一般的Windows消息(WM_xxx),则一定是派生类流向基类,不考虑横流。
2.如果是命令消息,WM_COMMAND,应有奇特的流动路线。
奇特的流动路线:
命令消息接受者的类型 处理次序
Frame窗口:View–>Frame窗口本身–>CWinApp对象
View :View本身–>Document
Document: Document本身–>Document Template

模拟消息推动引擎:

类 与消息循环有关的成员函数 注意点 none AfxWndProc global none AfxCallWndProc global CCmdTarget OnCmdMsg virtual CDocument OnCmdMsg virtual CWnd WindowProc virtual OnCommand virtual DefWindowProc virtual CFrameWnd OnCommand virtual OnCmdMsg virtual CView OnCmdMsg virtual CCmdTarget OnCmdMsg virtual CCmdTarget OnCmdMsg virtual

AfxWndProc是推动引擎的起点,在CWinThread::Run中被调用

1.AfxWndProc用四个参数接受消息
2.执行pWnd->WindowProc(nMsg, wParam, lParam)【这里默认pWnd要指向CWnd或其派生类,可以看出一开始的接受方一定是CWnd或其派生类】,
3.CWnd::WindowProc,根据nMsg是不是WM_COMMAND,决定是采用逐层往上的消息传递处理方式还是OnCommand
4.依据pWnd动态类型决定,执行CFrameWnd::OnCommand【最后还是到CWnd::Command】还是CWnd::Command
5.CWnd::Command,在函数体中执行OnCmdMsg(wParam, lParam),CCmdTarget,CFrameWnd,CView都有自定义OnCmdMsg
6.根据pWnd决定执行那个的OnCmdMsg。
三者的OnCmdMsg处理各不相同

// 可看出每个CFrameWnd至少有一个对应的CViewBOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode){    CView* pView = GetActiveView();    if(pView->OnCmdMsg(nID, nCode))        return TRUE;    if(CWnd::OnCmdMsg(nID, nCOde))        return TRUE;    CWinApp* pApp = AfxGetApp();    if(pApp->OnCmdMsg(nID, nCode))        return TRUE;    return FALSE;}// 可看出每个CView必有对应的CDocumentBOOL  CView::OnCmdMsg(UINT nID, int nCode){    if(CWnd::OnCmdMsg(nID, nCode))        return TRUE;    BOOL bHandled  = FALSE;    bHandled = m_pDocument->OnCmdMsg(nID, nCode);    return bHandled;}BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode){    AFX_MSGMAP* pMessageMap;    AFX_MSGMAP_ENTRY* lpEntry;    // GetMessageMap是虚函数,这里遍历比较的起点依然根据一开始的hWnd的动态类型决定。    for(pMessageMap = GetMessageMap(); pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMessageMap)    {        lpEntry = pMessageMap->lpEntries;        // 根据lpEntry可以遍历对应类的消息映射表,比较消息ID,如找到匹配的执行对应的消息处理函数。同时结束过程。找不到依次往上找    }    return FALSE;}

总的来说,消息一开始被谁获得,决定了此消息接下去怎么走他的传递路线。
如被CFrameWnd或其派生类对象获取,
把消息交给与此CFrameWnd或其派生类相关联的活动CView对象处理【从此CView或CView派生类对象逐个往上找对应类的消息映射表】,若处理不了。
将此消息传递给与相关联的CView对象的相关联的CDocument对象处理【从此CDocument或其派生类对象逐个往上找对应类的消息映射表】,若依然处理不了。
将此消息传给CFrameWnd对象处理【从此CFrameWnd或其派生类对象逐个往上找对应类的消息映射表】,若处理不了。
将此消息传给全局App对象处理【从CWinApp或其派生类对象逐个往上找对应类的消息映射表】,若还是处理不了
用DefWindowProc处理。

如被CView或其派生类对象获取,
上面已包含此过程。

0 0
原创粉丝点击