MFC消息响应原理和深入分析的整理
来源:互联网 发布:mac照片导u盘 编辑:程序博客网 时间:2024/05/21 23:50
首先对于这个映射机制有点类似一个表。就是一个消息对应于一个消息处理函数,这也就是映射。这里要知道MFC的一个窗口创建过程
一、MFC的入口点与简单的执行过程
MFC隐藏了windows程序的入口点winMain,其实是在appmodule.cpp文件下,该入口点调用MFC的全局函数AfxWinMain作为MFC的入口点
然后AfxWinMain会进行一些初始化操作,并执行全局CWinApp的InitInstance函数,即我们重写的一个虚函数
在CWinApp函数中,会初始化我们的窗口指针,调用了该窗口的构造函数,在构造函数中会有一个Create函数,该函数会注册一个窗口类,但是还没有类名和窗口过程,接着会执行一个PreCreateWindow的函数,处理好后,会给一个类名和一个DefWindowProc,并注册好这个窗口类,紧接着,会执行AfxHookWindowCreate函数,作用是将该窗口的窗口过程变为AfxWindowProc,即为一个全局的窗口过程函数,即MFC下所有窗口都共享一个窗口过程。窗口类注册好后,就用CreateWindow的api函数创建了一个窗口,窗口的创建就完成了。这是由入口点大概的执行流程,中间也忽略了不少函数,但是差不多就是windows sdk写程序的大概流程了,注册窗口类之类的全在里面了。
最主要的还是窗口的消息循环,由上述步骤可以发现,MFC的窗口的WindowProc是一个函数,即全局的AfxWindowProc,接下来记录下MFC的消息流向即消息映射。
先说明要产生消息的映射的几个步骤:
第一步:
这里是MFC窗体所共有的窗口过程,即任何窗体接受到的消息,最终都会流向这里,首先 该函数会由消息的接受句柄来得到它的窗体指针,接着会调用AfxCallWndProc函数.还有我们可以看到return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); 这个函数。接下来就去看看这个函数
第二步:
- LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
- WPARAM wParam = 0, LPARAM lParam = 0)
- {
- ......
- lResult = pWnd->WindowProc(nMsg, wParam, lParam);
- // more special case for WM_INITDIALOG
- if (nMsg == WM_INITDIALOG)
- _AfxPostInitDialog(pWnd, rectOld, dwStyle);
- }
- CATCH_ALL(e)
- {
- lResult = AfxProcessWndProcException(e, &pThreadState->m_lastSentMsg);
- TRACE(traceAppMsg, 0, "Warning: Uncaught exception in WindowProc (returning %ld).\n",
- lResult);
- DELETE_EXCEPTION(e);
- }
- END_CATCH_ALL
- pThreadState->m_lastSentMsg = oldState;
- return lResult;
- }
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,WPARAM wParam = 0, LPARAM lParam = 0){_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();MSG oldState = pThreadState->m_lastSentMsg; // save for nestingpThreadState->m_lastSentMsg.hwnd = hWnd;pThreadState->m_lastSentMsg.message = nMsg;pThreadState->m_lastSentMsg.wParam = wParam;pThreadState->m_lastSentMsg.lParam = lParam;#ifdef _DEBUG_AfxTraceMsg(_T("WndProc"), &pThreadState->m_lastSentMsg);#endif// Catch exceptions thrown outside the scope of a callback// in debug builds and warn the user.LRESULT lResult;TRY{#ifndef _AFX_NO_OCC_SUPPORT// special case for WM_DESTROYif ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))pWnd->m_pCtrlCont->OnUIActivate(NULL);#endif// special case for WM_INITDIALOGCRect rectOld;DWORD dwStyle = 0;if (nMsg == WM_INITDIALOG)_AfxPreInitDialog(pWnd, &rectOld, &dwStyle);// delegate to object's WindowProclResult = pWnd->WindowProc(nMsg, wParam, lParam);// more special case for WM_INITDIALOGif (nMsg == WM_INITDIALOG)_AfxPostInitDialog(pWnd, rectOld, dwStyle);}CATCH_ALL(e){lResult = AfxProcessWndProcException(e, &pThreadState->m_lastSentMsg);TRACE(traceAppMsg, 0, "Warning: Uncaught exception in WindowProc (returning %ld).\n",lResult);DELETE_EXCEPTION(e);}END_CATCH_ALLpThreadState->m_lastSentMsg = oldState;return lResult;}
我们只看 lResult = pWnd->WindowProc(nMsg, wParam, lParam); 这句,中间代码有省略。。。 其实发现这里又回到了产生消息的窗口处理过程中,AfxWindowProc相当于一个中转,然后根据消息的类型(这里消息是和句柄有关,一个窗口产生消息,那么这个消息是和这个窗口句柄有关,同时,窗口句柄有个窗口类指针有关。相当于这个消息是关联这这个窗口类的指针,这里要正确理解窗口和窗口类的区别) 转发到消息的关联窗口去,然后该窗口类调用 pWnd->WindowProc
而且我们可以发现这个WindowProc 是个虚函数,会调用基类的 WindowProc 。本身它也是从基类继承过来的。接下来就看看这个函数吧:
- LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
- {
- // OnWndMsg does most of the work, except for DefWindowProc call
- LRESULT lResult = 0;
- if (!OnWndMsg(message, wParam, lParam, &lResult))
- lResult = DefWindowProc(message, wParam, lParam);
- return lResult;
- }
DECLARE_MESSAGE_MAP(),BEGIN_MESSAGE_MAP,END_MESSAGE_MAP 其实也没什么因为这个OnWndMsg()只不过是调用他们定义的函数而已。
嘿嘿。。。待会后面具体来看看怎么个调用法。这里先吧消息的执行过程走一遍。
这个OnWndMsg(message, wParam, lParam, &lResult));是对消息进行处理,并且调用相应的处理函数。如果没有,则调用窗口默认的 DefWindowProc(message, wParam, lParam);消息过程就这样走完了。但是这里并没有看到消息映射啊? 其实刚刚已经提到了,OnWndMsg(message, wParam, lParam, &lResult)),这里就是包含了消息的映射了。消息处理过程涉及到多态,这里需要把握看这些函数具体调用的是基类的还是产生消息的窗口类。
接下来我们就看看
二、消息的响应机制
OnWndMsg(message, wParam, lParam, &lResult))这个函数是怎么样一个消息映射。
一般我们创建消息映射的3个步骤.忘记了??
http://www.cnblogs.com/zengxiangchun/archive/2012/04/27/2473080.html 复习一下吧!!!
1、消息响应函数原型(以WM_LBUTTON来说)
//{{AFX_MSG(CMyView)afx_msg void OnLButtonDown(UINT nFlags,CPoint point);//}}AFX_MSGDECLARE_MESSAGE_MAP()
这里首先对消息处理函数 OnLButtonDown()的一个声明
其次是定义了一个宏 DECLARE_MESSAGE_MAP() 。
2、定义的4个宏
DECLARE_MESSAGE_MAPBEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
先看DECLARE_MESSAGE_MAP()protected:
static const AFX_MSGMAP* PASCAL GetThisMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;
可以发现 声明了2个函数, GetMessageMap() , GetThisMessageMap() 返回类型是AFX_MSGMAP* ,这两个函数的作用就是获得指向MSGMAP的指针,每一个窗口类都会有这样一个 MSGMAP。AFX_MSGMAP是一种数据结构类型,看下它的定义吧:
struct AFX_MSGMAP
{
const AFX_MSGMAP* (* pfnGetBaseMap)();//这里是获得基类的 MSGMAP*
const AFX_MSGMAP_ENTRY* lpEntries;//本类的 AFX_MSGMAP_ENTRY*
};
再看
BEGIN_MESSAGE_MAP()END_MESSAGE_MAP()
这两个宏是连在一起看的。重写基类的 GetMessageMap() ?哪个重写? 当然是产生消息的那个窗口类呗。这里是CmainFrame::
最后看 ON_WM_LBUTTONDOWN()
{ WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_PMSG) (AFX_PMSGW)(static_cast
< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > ( &ThisClass :: On
Create)) } 可以发现这个宏表示了 ON_WM_CREATE()和OnCreate()处理函数想关联,而且你可以发现这个宏是放在 AFX_MSGMAP_ENTRY _messageEntries[](这是一个MessageEntry的数组,包含许多消息实体,譬如刚刚说的ON_WM_LBUTTONDOWN()) 中,所以只需要查找到了本窗口类的messageEntries[],再从中查找对应于Message消息的MessageEntry就能够找到相应的处理函数。这里就是后面先从本窗口类messageEntries[] 中来查找Message。
上面都是一些初始化的代码。这里要记住DECLAE_ MESSAGE_MAP声明了2个函数GetMessageMap() , GetThisMessageMap() ,然后
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
重写GetMessageMap(),获得指向AFX_MSGMAP 的指针 。什么是AFX_MSGMAP ? 忘了? 再看看上面的定义吧,就是一个结构体,包含本窗口类的AFX_MSGMAP_ENTRY和基类的AFX_MSGMAP的指针啦。。。
好啦。上面的一些结构体的定义和函数声明定义搞清楚了。接下来就要进入
OnWndMsg(message, wParam, lParam, &lResult)) 啦。。。。 先看函数
其中的pMessageMap = GetMessageMap();语句要留意,因为GetMessageMap是虚函数,在上面的CmainFrame类中已经重写了,所以,调用的实际上是CmainFrame的GetMessageMap;返回一个本窗口类的 AFX_MSGMAP指针 pMessageMap,这个MSGMAP包含了 基类的MSGMAP指针和本窗口类的_messageEntries的入口地址。所以我们可以知道
for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap)
if (message < 0xC000)
if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL)
goto LDispatch;
AfxFindMessageEntry就是在找message和_messageEntries.nMessage的相等,如果不等,就往基类里面找: pMessageMap = pMessageMap->pBaseMap
找到了就跳到Ldispatch,其中,有:mmf.pfn = lpEntry->nPfn;这个就是放着该消息处理函数的内容。这里是一个函数指针。
所以我们到现在应该知道
OnWndMsg(message, wParam, lParam, &lResult));这个函数是怎么样一个消息映射吧。通俗一点讲就是,先从本窗口类的AFX_MSGMAP(这个MSGMAP包含了 基类的MSGMAP指针和本窗口类的_messageEntries的入口地址)中的_messageEntries来找,如果没有找到Message对应的MessageEntry,那么就从对应的基类开始找。如果找到了,那么这个MessageEntry就包含处理函数指针,只需要调用这个函数处理就行了。这就是消息的映射。综合上面的消息的处理流程和消息的映射机制。对MFC的消息处理应该不陌生吧。嘿嘿,大家一起共进步吧!!!
这里也只是整理了一下下面2为大牛的学习笔记。有什么不明白直接看他们2个的学习笔记吧!!
http://blog.csdn.net/jiangnanyouzi/article/details/3722203
http://blog.csdn.net/sryan/article/details/7392814
- MFC消息响应原理和深入分析的整理
- 深入解析MFC消息响应和消息路由
- 深入解析MFC消息响应和消息路由
- 深入解析MFC消息响应和消息路由
- 深入解析MFC消息响应和消息路由
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- mfc 消息响应机制分析
- MFC消息响应机制分析
- MFC消息响应机制分析
- mysql 备份批处理
- java中String Date Timestamp Calendar 之间的关系及转换
- Python简明教程学习笔记4--数据结构
- linux系统文件夹权限
- WCDMA/CDMA/TDSCDMA/GSM各自指什么
- MFC消息响应原理和深入分析的整理
- Access2007导入文本文件和Excel文件时,出现的三种错误情况及其解决
- 安卓退出程序
- 破解APK并注入自己的代码
- 毕业设计简述
- 守护进程
- QT exe发布
- eclipse_导入sun.misc包出错
- CAD中组(Group)如何应用,编组和图块有何区别?