【滴水基礎(chǔ)】5.MFC(2)
第九:MFC源碼分析

#MFC的WinMian分析
---注意:CWinThread* pThread = AfxGetThread()和CWinApp* pApp = AfxGetApp();
---都是獲取的全局對(duì)象theApp的虛擬內(nèi)存地址:因?yàn)橐粋€(gè)進(jìn)程里面存在一個(gè)主線程,AfxGetThread()返回的是當(dāng)前界面線程對(duì)象的指針。
---AfxGetApp()返回的是應(yīng)用程序?qū)ο?/strong>的指針,如果該應(yīng)用程序(或進(jìn)程)只有一個(gè)界面線程在運(yùn)行,那么這兩者返回的都是一個(gè)全局的應(yīng)用程序?qū)ο笾羔槨?br>
---查看pApp:指向的是CMyApp的首地址
---但是后面還有父類CWinThread,已經(jīng)CWinApp的CRuntimeClass結(jié)構(gòu)體classCWinApp的地址(支持RTTI和動(dòng)態(tài)創(chuàng)建)
---當(dāng)然也存在:當(dāng)前類的消息消息映射結(jié)構(gòu)體_messageEntries和獲取自己和父類消息映射的messageMap

---AfxGetApp()獲取當(dāng)前進(jìn)程的位置(AfxGetThread()本質(zhì)也是調(diào)用AfxGetApp())
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp(){return afxCurrentWinApp; }
---而afxCurrentWinApp是個(gè)宏定義,位于src\..\..\atlmfc\include\afxwin.h
---AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow)將參數(shù)傳遞給當(dāng)前的全局對(duì)象theApp的成員變量中
---總結(jié):
---創(chuàng)建全局對(duì)象theApp,然后進(jìn)入AfxWinMain()
---通過(guò)AfxGetApp()獲取當(dāng)前進(jìn)程/線程的內(nèi)存地址(theApp的地址)
---通過(guò)AfxInit()將AfxWinMain的四個(gè)參數(shù)賦值給theApp的成員變量
---在InitInstance創(chuàng)建窗口對(duì)象后,會(huì)調(diào)用窗口對(duì)象(CFrameWnd)的構(gòu)造函數(shù)
---在構(gòu)造函數(shù)里面調(diào)用Create()的CreateEx()創(chuàng)建窗口
---里面先用PreCreateWindow()自定義窗口類和注冊(cè)窗口類(本質(zhì)是Windows的HWND窗口)
---然后調(diào)用AfxHookWindowCreate創(chuàng)建消息鉤子,所有DispatchMessage消息被_AfxCbtFilterHook截獲
---SetWindowLong設(shè)置窗口過(guò)程為AfxWndProc,并保存原窗口過(guò)程在窗口類成員變量m_pfnSuper中,這樣形成一個(gè)窗口過(guò)程鏈,將MFC的窗口對(duì)象和Windows的窗口對(duì)象進(jìn)行綁定
---Windows消息送給AfxWndProc窗口過(guò)程之后,AfxWndProc得到HWND窗口對(duì)應(yīng)的MFC窗口對(duì)象(CFrameWnd的子類)。
---然后調(diào)用OnWndMsg搜索根據(jù)本對(duì)象的messageMap,迭代搜索該MFC窗口對(duì)象和其基類的消息映射數(shù)組_messageEntries[]
---最后,判定它們是否處理當(dāng)前消息,如果是則調(diào)用對(duì)應(yīng)的消息處理函數(shù),否則,進(jìn)行缺省處理

#為什么在InitInstance()里面要Return True?
---消息循環(huán)(CWinThread是CWinApp的父類)
---進(jìn)入CWinThread::Run()
---在Win32中的WinMian中,通過(guò)GetMessage獲取消息,注意GetMessage()是一個(gè)具有同步行為的函數(shù),如果消息隊(duì)列里面沒(méi)有消息,會(huì)一直等待,直到消息的出現(xiàn)才返回
---而PeekMessage():不管消息隊(duì)列里面是否存在消息,都會(huì)立即返回(就不會(huì)導(dǎo)致線程出現(xiàn)睡眠狀態(tài),注意:睡眠狀態(tài)下的線程下面的代碼不會(huì)執(zhí)行)
---如果消息隊(duì)列中沒(méi)有消息,PeekMessage()返回0,不會(huì)導(dǎo)致線程處于睡眠狀態(tài)。
---對(duì)于virtual BOOL?OnIdle( LONG lCount );
---返回值:如果要接收更多的空閑處理時(shí)間,則返回非零值;如果不需要更多的空閑時(shí)間則返回0。
---參數(shù):lCount 該參數(shù)是一個(gè)計(jì)數(shù)值,當(dāng)應(yīng)用程序的消息隊(duì)列為空,OnIdle函數(shù)被調(diào)用時(shí),該計(jì)數(shù)值就增加1。
---每當(dāng)一條新消息被處理時(shí),該計(jì)數(shù)值就被復(fù)位為0。你可以使用lCount參數(shù)來(lái)確定應(yīng)用程序不處理消息時(shí)空閑時(shí)間的相對(duì)長(zhǎng)度。
---本身OnIdle( LONG lCount )是一個(gè)虛函數(shù),如果想要執(zhí)行空閑時(shí)處理,則重載這個(gè)成員函數(shù)。
---Run()是MFC的控制中心,而PumpMessage()又是Run()的核心,所以從MFC的真正控制中心是PumpMessage()
---PumpMessage()的核心:PreTranslateMessage(&m_msgCur)
---查看PreTranslateMessage(&m_msgCur)
---正是有了PreTranslateMessage(),才使得MFC能夠靈活的控制消息的分發(fā)模式,可以說(shuō),PreTranslateMessage()就是MFC的消息分發(fā)模式
---根據(jù)不同的窗口(桌面、MFC、Dialog)進(jìn)行處理
---查看WalkPreTranslateTree()消息進(jìn)行處理
---作用:將傳統(tǒng)的Win32的窗口過(guò)程函數(shù),改為消息映射,而WalkPreTranslateTree()的作用就是
#Run()總結(jié)
---只有MFC窗口(繼承于CFrameWnd)才會(huì)WalkPreTranslateTree()遍歷窗口的父類
---其它窗口如桌面直接DispatchMEssage、dialog窗口直接PreTranslateMessage()
---Windows窗口(hwnd)一般很少有父類,我猜測(cè)這里的hWnd = ::GetParent(hWnd)是MFC窗口的父類(如CMainWindow和父類CFrameWnd),而不是Windows窗口(hwnd)的父類

---再次查看代碼:
---剛剛的猜測(cè)錯(cuò)誤,這里就是遍歷Windows窗口的父類
---然后獲取Window窗口對(duì)應(yīng)的MFC窗口映射
---如果MFC窗口能處理這個(gè)消息,就返回TRUE,那么在CWnd::PreTranslateMessage()里面就不需要TranslateMessage和DispatchMessage
---而是通過(guò)MFC的消息映射,用MFC的CFrameWnd類(子類)->PreTranslateMessage(pMsg)來(lái)對(duì)消息進(jìn)行處理
#問(wèn)題:在BOOL CMyApp::InitInstance()中,m_pMainWnd=(CFrameWnd*)new CMainWindow是在堆中分配內(nèi)存,但是沒(méi)有釋放內(nèi)存,容易造成內(nèi)存泄露
---但是窗口關(guān)閉并沒(méi)有照成問(wèn)題Why?CMainWindow自己對(duì)自己進(jìn)行了刪除
---繼承于CWnd,發(fā)現(xiàn)是一個(gè)虛函數(shù),代碼就是:delete this

---在CMainWindows重寫(xiě)PostNcDestroy(),調(diào)用父類的PostNcDestroy(),下斷點(diǎn)
---點(diǎn)擊窗口右上角的關(guān)閉,產(chǎn)生WM_NCDESTROY的消息
---就會(huì)CMainWindows遍歷父子類,對(duì)該消息進(jìn)行處理(emmm我這里沒(méi)辦法進(jìn)入CFrameWnd調(diào)試)
---注意:如果窗口基礎(chǔ)于CWnd,則需要自己去重寫(xiě)PostNcDestroy(),并且執(zhí)行delete this
#總結(jié)
---全局對(duì)象theApp(早于WinMain執(zhí)行),然后進(jìn)入AfxWinMain()
---AfxGetApp()獲取當(dāng)前進(jìn)行的內(nèi)存地址(也是theApp的地址)
---AfxWinInit()將AfxWinMain的四個(gè)參數(shù)賦值給theApp的成員變量,創(chuàng)建當(dāng)前應(yīng)用程序主線程
---InitInstance() 被重寫(xiě),其成員變量m_pMainWnd指向new的窗口對(duì)象CMainWindow(繼承于CFrameWnd)
---new窗口對(duì)象時(shí),調(diào)用構(gòu)造函數(shù),在里面創(chuàng)建窗口:Create(NULL,"主窗口")
---Create的本質(zhì)是調(diào)用CreateEx(),在里面先創(chuàng)建一個(gè)CREATESTRUCT結(jié)構(gòu)體,用來(lái)存儲(chǔ)需要創(chuàng)建窗口的參數(shù),先調(diào)用PreCreateWindow(cs),如果是Windows默認(rèn)的窗口類(窗口類名為NULL),就調(diào)用AfxDeferRegisterClass對(duì)窗口進(jìn)行注冊(cè),并且賦默認(rèn)值。(默認(rèn)類名:_afxWndFrameOrView是一個(gè)char類型的數(shù)組,值為"AfxFrameOrView140sud",是MFC中l(wèi)pszClass的默認(rèn)名
)
---在AfxDeferRegisterClass中,創(chuàng)建窗口對(duì)象,給窗口對(duì)象設(shè)置:窗口模塊句柄(hInstance),默認(rèn)窗口過(guò)程函數(shù)(如果沒(méi)有消息響應(yīng)函數(shù)),根據(jù)參數(shù)設(shè)置窗口風(fēng)格,然后注冊(cè)窗口
---PreCreateWindow(cs)之后,用AfxHookWindowCreate(this) ,將Windows窗口的消息,在DispatchMessage之后,_AfxCbtFilterHook在窗口過(guò)程函數(shù)之前接受消息
---_AfxCbtFilterHook調(diào)用Attach()將Windows窗口句柄(hwnd)和MFC窗口類(CFrameWnd派生類)進(jìn)行綁定,將窗口的消息處理函數(shù)改為afxWndProc
---Attach將hwnd和CFrameWnd進(jìn)行綁定,Windows根據(jù)hwnd找到CFrameWnd
---將映射關(guān)系保存道線程的(theApp)的m_pmapHWNDh成員變量中
---也即是:原來(lái)DispatchMessage后,發(fā)送給窗口過(guò)程函數(shù)的消息,現(xiàn)在發(fā)送給afxWndProc,AfxWndProc得到HWND窗口對(duì)應(yīng)的MFC窗口對(duì)象
---這樣一個(gè)框架類就可以代表一個(gè)窗口,符合面向?qū)ο?/strong>的編程思想
---最后,調(diào)用hWnd = ::CreateWindowEx()創(chuàng)建了窗口(和Win32CreateWindow()一樣)
#窗口的分類

#窗口的創(chuàng)建流程
---模塊和進(jìn)程的區(qū)別:一個(gè)進(jìn)程可以加載多個(gè)模塊,而模塊里面可以是一些代碼

#消息和消息隊(duì)列

---查看AfxWndProc()
---通過(guò)窗口句柄獲取CFrameWnd對(duì)象的指針pFrame
---通過(guò)pFrame指針調(diào)用CFrameWnd的WindowProc函數(shù)進(jìn)行窗口消息的處理
---CFrameWnd的WindowProc是繼承于CWnd::WindowProc
---注意:CObject? > CCmdTarget > CWnd > CFrameWnd
---CObject? > CCmdTarget > CWinThread > CWinApp
------在OnWndMsg循環(huán)搜索本對(duì)象和父類對(duì)象對(duì)應(yīng)的消息映射數(shù)組messageMap,找到對(duì)應(yīng)的_messageEntries中對(duì)應(yīng)的消息響應(yīng)函數(shù)(沒(méi)有就調(diào)用DefWindowProc默認(rèn)處理)
---然后MFC窗口類的消息響應(yīng)函數(shù)對(duì)消息進(jìn)行處理
---如下(注意:消息響應(yīng)函數(shù)的命名要和消息進(jìn)行對(duì)應(yīng))
---MFC的窗口類之間,的消息映射機(jī)制如下
---每個(gè)MFC窗口子父類通過(guò)messageMap關(guān)聯(lián)
---MFC窗口對(duì)應(yīng)一個(gè)_messageEntries結(jié)構(gòu)體(類型記錄鏈表),只能通過(guò)messageMap訪問(wèn),記錄了消息ID和對(duì)應(yīng)的消息響應(yīng)函數(shù)

#在重寫(xiě)InitInstance()窗口創(chuàng)建之后,AfxWinMain()還帶調(diào)用了Run()進(jìn)行消息循環(huán)
---對(duì)于AfxWinMain()來(lái)個(gè)簡(jiǎn)單的總結(jié)
---消息DispatchMessage后,發(fā)送到AfxWndProc()窗口過(guò)程
---AfxWndProc()根據(jù)hWnd和MFC窗口的匹配,調(diào)用MFC窗口的WindowProc()方法
---WindowProc()通過(guò)OnWndMsg遍歷MFC窗口的子父類,找消息響應(yīng)函數(shù),對(duì)消息進(jìn)行處理

#問(wèn)題:
---MFC中的消息循環(huán)機(jī)制Run()有點(diǎn)不明白
---WalkPreTranslateTree()找到Windows窗口對(duì)應(yīng)可以處理消息的MFC窗口,CWinThread::PreTranslateMessage(MSG* pMsg)返回TRUE
---BOOL CWinThread::PumpMessage()返回TRUE,主線程又在消息隊(duì)列里面獲取下一跳消息
---我個(gè)人覺(jué)得Run()存在2個(gè)作用:1.不同的從消息隊(duì)列獲取消息 2.確保消息隊(duì)列獲取的消息,存在的MFC窗口(hwnd對(duì)應(yīng))可以處理它,如果不能處理,或者不存在和消息hwnd對(duì)應(yīng)的MFC窗口類,就直接發(fā)送給hwnd默認(rèn)的窗口函數(shù)處理