当前位置:首页 > Windows运行原理以及MFC框架分析
}
return TRUE; }
::TranslateMessage(&m_msgCur); //进行消息(如键盘消息)转换
::DispatchMessage(&m_msgCur); //分派消息到窗口的回调函数处理(实际上分派的消息经过消息
映射,交由消息响应函数进行处理。
首先,PumpMessage()调用GetMessage()从消息队列中取一条消息,由于PumpMessage()是在消息队列中有消息的时候才被调用的,所以GetMessage()会马上返回,根据其返回值,判断当前取出的消息是不是WM_QUIT【这个描述好像有些问题】消息(这个消息一般对是通过调用PostQuitMessage()放入线程消息队列的),如果是,就返回FALSE,CWinThread::Run()该退出了, CWinThread::Run()直接调用CWinThread::ExitInstance()退出应用程序。在GetMessage()的后面是我们所熟悉的TranslateMessage()和DispatchMessage()函数。
可以看出,是否调用TranslateMessage()和DispatchMessage()是由一个名称为PreTranslateMessage()函数的返回值决定的,如果该函数返回TRUE,则不会把该消息分发给窗口函数处理。
就我个人观点而言,正是有了这个PreTranslateMessage(),才使得MFC能够灵活的控制消息的分发模式,可以说,PreTranslateMessage()就是MFC的消息分发模式。
经过层层扒皮,终于找到了CWinThread::Run()最具特色的地方,这就是PreTranslateMessage()函数。同前面使用Windows SDK编写的WinMain函数程序的消息循环不同的地方在于,MFC多了这个PreTranslateMessage(),PreTranslateMessage()最先获得了应用程序的消息处理权!下面我们对PreTranslateMessage()进行剥皮式分析。同前面一样,首先看看实际的 PreTranslateMessage()的代码:
BOOL CWinThread::PreTranslateMessage(MSG* pMsg) {
ASSERT_VALID(this);
// if this is a thread-message, short-circuit this function if (pMsg->hwnd == NULL &&DispatchThreadMessageEx(pMsg)) return TRUE;
// walk from target to main window CWnd* pMainWnd = AfxGetMainWnd();
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
// in case of modeless dialogs, last chance route through main
25
return TRUE;
// window's accelerator table if (pMainWnd != NULL) {
CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd); returnpMainWnd->PreTranslateMessage(pMsg);
if (pWnd->GetTopLevelParent() != pMainWnd) }
return FALSE; // no special processing }
PreTranslateMessage()的处理过程如下:首先判断该消息是否是一个线程消息(消息的
窗口句柄为空的消息),如果是,交给DispatchThreadMessageEx()处理。我们暂时不管DispatchThreadMessageEx(),它不是我们讨论的重点。
调用CWnd::WalkPreTranslateTree()对该消息进行处理,注意该函数的一个参数是线程主窗口的句柄,这是PreTranslateMessage()的核心代码,在后面会对这个函数进行详细的分析。
下面详细讨论一下CWnd::WalkPreTranslateTree()函数,它的代码很简单:
BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg) {
ASSERT(hWndStop == NULL || ::IsWindow(hWndStop)); ASSERT(pMsg != NULL);
// walk from the target window up to the hWndStop window checking // if any window wants to translate this message
for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) {
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd); if (pWnd != NULL) { }
// got to hWndStop window without interest if (hWnd == hWndStop) }
}
return FALSE; // no special processing
break;
// target window is a C++ window
if (pWnd->PreTranslateMessage(pMsg))
return TRUE; // trapped by target window (eg: accelerators)
26
CWnd:: WalkPreTranslateTree()的所使用的策略很简单,拥有该消息的窗口最先获得该消息的处理权,如果它不想对该消息进行处理(该窗口对象的PreTranslateMessage()函数返回FALSE),就将处理权交给它的父亲窗口,如此向树的根部遍历,直到遇到hWndStop(在CWinThread::PreTranslateMessage()中,hWndStop表示的是线程主窗口的句柄)。记住这个消息处理权的传递方向,是由树的某个一般节点或叶子节点向树的根部传递!
MFC消息控制流最具特色的地方是CWnd类的虚拟函数PreTranslateMessage(),通过重载这个函数,我们可以改变MFC的消息控制流程,甚至可以作一个全新的控制流出来,在下面的一章会对MFC的实现作详细介绍。
只有穿过消息队列的消息才受PreTranslateMessage()影响,采用SendMessage()或其他类似的方式向窗口直接发送的而不经过消息队列的消息根本不会理睬PreTranslateMessage()的存在
传给PreTranslateMessage()的消息是未经翻译过的消息,它没有经过TranslateMessage()处理,在某些情况下,要仔细处理,以免漏掉消息
3.3.7 窗口过程函数
现在已经进入消息循环了,那么MFC程序是否也把消息路由给一个窗口过程函数中去呢?我们回头看3.3.3注册窗口类时,在AfcEndDeferRegisterClass函数中指定的函数是DefWindowProc。该函数调用缺省的窗口过程来为应用程序没有处理的任何窗口消息提供缺省的处理。该函数确保每一个消息得到处理。WindowProc是你给自己的窗口定义的窗口处理函数,DefWindowProc是windows平台提供的默认窗口处理函数,如果某些消息你不需要做特别的处理,调用DefWindowProc进行处理就可以了,不需要你自己再去些那些windows的\标准动作\。
至此,我们就了解了MFC程序的整个运行机制,实际上与Win32 SDK程序是一致的,它同样也需要经过:设计窗口类(只不过MFC程序中已经预定义了一些窗口类,我们可以直接使用),注册窗口类,创建窗口,显示并更新窗口,消息循环。
3.4 文档与视图结构框架分析
这里我们新建一个工程名为Draw的多文档视图结构(MDI),MFC AppWizard会为我们生成如下图3.4.1所示类框架,该程序可以直接运行,无需再写任何代码。虽然我们没有编写一行代码,但是可以看出由系统自动生成的应用程序的界面已经有了一个标准WINDOWS应
27
用程序所需的几个组成部分,我们要做的事情是往这个应用程序添加必要的代码以完成我们
所需要的功能。
采用文档/视图结构最大的优点在于将程序数据本身与数据的操作与维护分离开来。微软提供了一个相当灵活的结构,用户几乎可以采用这种结构创建任何类型的Windows程序。一个文档可以对应多个视图,比如,用户在一个图表中查看由数据生成的图表的同时,可以在另一个表单中查看产生该图表的原始数据。下面我们就对文档视图结构做详细分析。
通过对应用程序Draw进行分析,可以知道普通的MFC应用程序包含以下五个主要的类:文档类(CDrawDoc)、视图类(CDrawView)、主框架窗口类(CMainFrame)、子框架窗口类(CChildFrame)、应用类(CDrawApp),AppWizard为每个类的实现产生了各自的源文件。
3.4.1 应用类
应用程序Draw的应用类是CDrawApp,它是由MFC的CWinApp类派生的。其头文件是draw.h,实现文件是draw.cpp。
应用类管理程序的总体,它完成和其它类不一样的工作,我们前面讲到的全局对象theApp就是由这个类创建的,主要完成初始化程序以及进行最后的程序清除工作。
每个MFC应用程序必须正确的生成由CWinApp派生类的一个实例(对象)。
3.4.2 文档类
应用程序Draw的文档类的名称是CDrawDoc,这是AppWizard根据项目名称默认所取
28
共分享92篇相关文档