您的位置:首页 > 其它

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

2015-10-06 22:53 302 查看

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

模拟消息推动引擎:

与消息循环有关的成员函数注意点
noneAfxWndProcglobal
noneAfxCallWndProcglobal
CCmdTargetOnCmdMsgvirtual
CDocumentOnCmdMsgvirtual
CWndWindowProcvirtual
OnCommandvirtual
DefWindowProcvirtual
CFrameWndOnCommandvirtual
OnCmdMsgvirtual
CViewOnCmdMsgvirtual
CCmdTargetOnCmdMsgvirtual
CCmdTargetOnCmdMsgvirtual
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至少有一个对应的CView
BOOL 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必有对应的CDocument
BOOL  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或其派生类对象获取,

上面已包含此过程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: