全球报道:【滴水基础】5.MFC(2)

2023-03-25 20:00:58 来源:哔哩哔哩

第九: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默认的窗口函数处理

标签:

上一篇:
下一篇: