第九:MFC源码分析 MFC的WinMian分析---注意:CWinThread*pThread=AfxGetThread()和CWinApp*pApp=AfxGe...
1、一、WINXP 7系统Thinkpad笔记本首先将无线 蓝牙硬件开关向右拨动(此开关一般在机器前方或左右侧)...
1、《榜样--2008年度中央电视台迎奥运“希望之星”英语风采大赛参赛指导(小学组)》是田巍编辑剪辑,20...
一、饮水机的清洗方法饮水机使用一段时间之后是需要进行清洗消毒的。但是饮水机外部清洗比较简单,内部...
第九:MFC源码分析
#MFC的WinMian分析
---注意:CWinThread* pThread = AfxGetThread()和CWinApp* pApp = AfxGetApp();
(资料图片仅供参考)
---都是获取的全局对象theApp的虚拟内存地址:因为一个进程里面存在一个主线程,AfxGetThread()返回的是当前界面线程对象的指针。---AfxGetApp()返回的是应用程序对象的指针,如果该应用程序(或进程)只有一个界面线程在运行,那么这两者返回的都是一个全局的应用程序对象指针。
---查看pApp:指向的是CMyApp的首地址
---但是后面还有父类CWinThread,已经CWinApp的CRuntimeClass结构体classCWinApp的地址(支持RTTI和动态创建)
---当然也存在:当前类的消息消息映射结构体_messageEntries和获取自己和父类消息映射的messageMap
---AfxGetApp()获取当前进程的位置(AfxGetThread()本质也是调用AfxGetApp())
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp(){return afxCurrentWinApp; }
---而afxCurrentWinApp是个宏定义,位于src\..\..\atlmfc\include\afxwin.h
---AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)将参数传递给当前的全局对象theApp的成员变量中
---总结:
---创建全局对象theApp,然后进入AfxWinMain()
---通过AfxGetApp()获取当前进程/线程的内存地址(theApp的地址)
---通过AfxInit()将AfxWinMain的四个参数赋值给theApp的成员变量
---在InitInstance创建窗口对象后,会调用窗口对象(CFrameWnd)的构造函数
---在构造函数里面调用Create()的CreateEx()创建窗口
---里面先用PreCreateWindow()自定义窗口类和注册窗口类(本质是Windows的HWND窗口)
---然后调用AfxHookWindowCreate创建消息钩子,所有DispatchMessage消息被_AfxCbtFilterHook截获
---SetWindowLong设置窗口过程为AfxWndProc,并保存原窗口过程在窗口类成员变量m_pfnSuper中,这样形成一个窗口过程链,将MFC的窗口对象和Windows的窗口对象进行绑定
---Windows消息送给AfxWndProc窗口过程之后,AfxWndProc得到HWND窗口对应的MFC窗口对象(CFrameWnd的子类)。
---然后调用OnWndMsg搜索根据本对象的messageMap,迭代搜索该MFC窗口对象和其基类的消息映射数组_messageEntries[]
---最后,判定它们是否处理当前消息,如果是则调用对应的消息处理函数,否则,进行缺省处理
#为什么在InitInstance()里面要Return True?
---消息循环(CWinThread是CWinApp的父类)
---进入CWinThread::Run()
---在Win32中的WinMian中,通过GetMessage获取消息,注意GetMessage()是一个具有同步行为的函数,如果消息队列里面没有消息,会一直等待,直到消息的出现才返回
---而PeekMessage():不管消息队列里面是否存在消息,都会立即返回(就不会导致线程出现睡眠状态,注意:睡眠状态下的线程下面的代码不会执行)
---如果消息队列中没有消息,PeekMessage()返回0,不会导致线程处于睡眠状态。
---对于virtual BOOL OnIdle( LONG lCount );---返回值:如果要接收更多的空闲处理时间,则返回非零值;如果不需要更多的空闲时间则返回0。---参数:lCount该参数是一个计数值,当应用程序的消息队列为空,OnIdle函数被调用时,该计数值就增加1。---每当一条新消息被处理时,该计数值就被复位为0。你可以使用lCount参数来确定应用程序不处理消息时空闲时间的相对长度。
---本身OnIdle( LONG lCount )是一个虚函数,如果想要执行空闲时处理,则重载这个成员函数。
---Run()是MFC的控制中心,而PumpMessage()又是Run()的核心,所以从MFC的真正控制中心是PumpMessage()
---PumpMessage()的核心:PreTranslateMessage(&m_msgCur)
---查看PreTranslateMessage(&m_msgCur)
---正是有了PreTranslateMessage(),才使得MFC能够灵活的控制消息的分发模式,可以说,PreTranslateMessage()就是MFC的消息分发模式
---根据不同的窗口(桌面、MFC、Dialog)进行处理
---查看WalkPreTranslateTree()消息进行处理
---作用:将传统的Win32的窗口过程函数,改为消息映射,而WalkPreTranslateTree()的作用就是
#Run()总结
---只有MFC窗口(继承于CFrameWnd)才会WalkPreTranslateTree()遍历窗口的父类
---其它窗口如桌面直接DispatchMEssage、dialog窗口直接PreTranslateMessage()
---Windows窗口(hwnd)一般很少有父类,我猜测这里的hWnd = ::GetParent(hWnd)是MFC窗口的父类(如CMainWindow和父类CFrameWnd),而不是Windows窗口(hwnd)的父类
---再次查看代码:
---刚刚的猜测错误,这里就是遍历Windows窗口的父类
---然后获取Window窗口对应的MFC窗口映射
---如果MFC窗口能处理这个消息,就返回TRUE,那么在CWnd::PreTranslateMessage()里面就不需要TranslateMessage和DispatchMessage
---而是通过MFC的消息映射,用MFC的CFrameWnd类(子类)->PreTranslateMessage(pMsg)来对消息进行处理
#问题:在BOOL CMyApp::InitInstance()中,m_pMainWnd=(CFrameWnd*)new CMainWindow是在堆中分配内存,但是没有释放内存,容易造成内存泄露
---但是窗口关闭并没有照成问题Why?CMainWindow自己对自己进行了删除
---继承于CWnd,发现是一个虚函数,代码就是:delete this
---在CMainWindows重写PostNcDestroy(),调用父类的PostNcDestroy(),下断点
---点击窗口右上角的关闭,产生WM_NCDESTROY的消息
---就会CMainWindows遍历父子类,对该消息进行处理(emmm我这里没办法进入CFrameWnd调试)
---注意:如果窗口基础于CWnd,则需要自己去重写PostNcDestroy(),并且执行delete this
#总结
---全局对象theApp(早于WinMain执行),然后进入AfxWinMain()
---AfxGetApp()获取当前进行的内存地址(也是theApp的地址)
---AfxWinInit()将AfxWinMain的四个参数赋值给theApp的成员变量,创建当前应用程序主线程
---InitInstance() 被重写,其成员变量m_pMainWnd指向new的窗口对象CMainWindow(继承于CFrameWnd)
---new窗口对象时,调用构造函数,在里面创建窗口:Create(NULL,"主窗口")
---Create的本质是调用CreateEx(),在里面先创建一个CREATESTRUCT结构体,用来存储需要创建窗口的参数,先调用PreCreateWindow(cs),如果是Windows默认的窗口类(窗口类名为NULL),就调用AfxDeferRegisterClass对窗口进行注册,并且赋默认值。(默认类名:_afxWndFrameOrView是一个char类型的数组,值为"AfxFrameOrView140sud",是MFC中lpszClass的默认名
)
---在AfxDeferRegisterClass中,创建窗口对象,给窗口对象设置:窗口模块句柄(hInstance),默认窗口过程函数(如果没有消息响应函数),根据参数设置窗口风格,然后注册窗口
---PreCreateWindow(cs)之后,用AfxHookWindowCreate(this),将Windows窗口的消息,在DispatchMessage之后,_AfxCbtFilterHook在窗口过程函数之前接受消息
---_AfxCbtFilterHook调用Attach()将Windows窗口句柄(hwnd)和MFC窗口类(CFrameWnd派生类)进行绑定,将窗口的消息处理函数改为afxWndProc
---Attach将hwnd和CFrameWnd进行绑定,Windows根据hwnd找到CFrameWnd
---将映射关系保存道线程的(theApp)的m_pmapHWNDh成员变量中
---也即是:原来DispatchMessage后,发送给窗口过程函数的消息,现在发送给afxWndProc,AfxWndProc得到HWND窗口对应的MFC窗口对象
---这样一个框架类就可以代表一个窗口,符合面向对象的编程思想
---最后,调用hWnd = ::CreateWindowEx()创建了窗口(和Win32CreateWindow()一样)
#窗口的分类
#窗口的创建流程
---模块和进程的区别:一个进程可以加载多个模块,而模块里面可以是一些代码
#消息和消息队列
---查看AfxWndProc()
---通过窗口句柄获取CFrameWnd对象的指针pFrame
---通过pFrame指针调用CFrameWnd的WindowProc函数进行窗口消息的处理
---CFrameWnd的WindowProc是继承于CWnd::WindowProc
---注意:CObject > CCmdTarget > CWnd > CFrameWnd
---CObject > CCmdTarget > CWinThread > CWinApp
------在OnWndMsg循环搜索本对象和父类对象对应的消息映射数组messageMap,找到对应的_messageEntries中对应的消息响应函数(没有就调用DefWindowProc默认处理)
---然后MFC窗口类的消息响应函数对消息进行处理
---如下(注意:消息响应函数的命名要和消息进行对应)
---MFC的窗口类之间,的消息映射机制如下
---每个MFC窗口子父类通过messageMap关联
---MFC窗口对应一个_messageEntries结构体(类型记录链表),只能通过messageMap访问,记录了消息ID和对应的消息响应函数
#在重写InitInstance()窗口创建之后,AfxWinMain()还带调用了Run()进行消息循环
---对于AfxWinMain()来个简单的总结
---消息DispatchMessage后,发送到AfxWndProc()窗口过程
---AfxWndProc()根据hWnd和MFC窗口的匹配,调用MFC窗口的WindowProc()方法
---WindowProc()通过OnWndMsg遍历MFC窗口的子父类,找消息响应函数,对消息进行处理
#问题:
---MFC中的消息循环机制Run()有点不明白
---WalkPreTranslateTree()找到Windows窗口对应可以处理消息的MFC窗口,CWinThread::PreTranslateMessage(MSG* pMsg)返回TRUE
---BOOL CWinThread::PumpMessage()返回TRUE,主线程又在消息队列里面获取下一跳消息
---我个人觉得Run()存在2个作用:1.不同的从消息队列获取消息 2.确保消息队列获取的消息,存在的MFC窗口(hwnd对应)可以处理它,如果不能处理,或者不存在和消息hwnd对应的MFC窗口类,就直接发送给hwnd默认的窗口函数处理
标签: