【滴水基礎(chǔ)】5.MFC(1)
第一:MFC本質(zhì)
#MFC介紹(Microsoft Fundation Classes)
---由微軟提供的放置Win32Api的,面向?qū)ο蟮陌bC++類庫
---MFC中大約封裝了2000個(gè)類,分別封裝了WinApi和WinSDK中的結(jié)構(gòu)和過程
---另外,MFC提供了1個(gè)應(yīng)用程序框架,例如:程序向?qū)А㈩愊驅(qū)У淖灾a生成,提高了編碼效率
#Win32的Api、SDK和MFC區(qū)別
---API就是應(yīng)用程序接口,是由系統(tǒng)提供的一些函數(shù),比如你想創(chuàng)建一個(gè)文件,就要調(diào)用CreateFile,這個(gè)CreateFile就是一個(gè)API。
---SDK是指一些公司針對某一項(xiàng)技術(shù)為軟件開發(fā)人員制作的一套輔助開發(fā)的工具。一般專指Windows系統(tǒng)提供的相關(guān)的頭文件和LIB文件。
---MFC是MS對API的一個(gè)封裝,也就是一個(gè)C++類庫,當(dāng)然MFC比一般類庫龐大,所以有人稱之為應(yīng)用程序框架。但其本質(zhì)還是一個(gè)類庫
#MFC的本質(zhì)
---對于Win32Api和Win32SDK的封裝
#使用Win32創(chuàng)建窗口(窗口的消息流程)
---1.OS捕獲鍵盤輸入,傳遞給線程消息隊(duì)列(RegisterClass)
---2.線程不斷的獲取消息(GetMessage),對獲取的消息,進(jìn)行翻譯(TranslateMessage)為字符碼
---3.轉(zhuǎn)發(fā)(DispachMessage):線程浸入內(nèi)核,利用窗口句柄將不同的消息分發(fā)給各個(gè)窗口
---4.窗口調(diào)用窗口函數(shù),執(zhí)行特定的代碼

---相關(guān)的代碼
---窗口創(chuàng)建和消息傳遞處理的流程

---效果如下:可以放大、拖拽、關(guān)閉等

#VC6創(chuàng)建一個(gè)MFC窗口,命名HelloMFC

---采用基本對話框

---使用靜態(tài)鏈接庫,直接把庫編譯到exe文件

----可以自定義窗口

#總結(jié)(利用MFC寫一個(gè)窗口程序)
---優(yōu)點(diǎn):簡單、方便;缺點(diǎn):代碼冗余復(fù)雜
第二節(jié):第一個(gè)MFC程序
#本節(jié)需要掌握的知識點(diǎn):
---1.CWinApp可以覆蓋的虛函數(shù)InitInstance(在里面創(chuàng)建窗口)
---2.CWinApp成員變量:m_pMainWnd(相當(dāng)于WinMain函數(shù))
---3.CFramWnd的成員函數(shù):Create以及參數(shù)(相當(dāng)于窗口函數(shù))
#需要簡單了解的內(nèi)容
---1.通過MSDN去看MFC的層次結(jié)構(gòu)圖
---2.對CWinApp有初步的認(rèn)知
---3.對CFramWnd有初步的認(rèn)知
#MFC的層次結(jié)構(gòu)圖(Hierarchy Chart)

#CWinApp(應(yīng)用程序框架)
---提供了初始化應(yīng)用程序和運(yùn)行應(yīng)用程序的成員函數(shù)
---使用MFC的每個(gè)應(yīng)用程序只能包含1個(gè)WinApp的派生對象,
---當(dāng)你從WinApp派生應(yīng)用程序,覆蓋InitInstance成員函數(shù)以及創(chuàng)建應(yīng)用程序的主窗口對象
---InitInstance成員函數(shù)的成員變量:m_pMainWnd用來記錄主窗口對象
---BOOL InitInstance()是MFC的CWinApp類的成員函數(shù),而WinMain才是真正的入口點(diǎn),但是MFC不允許程序中有WinMain這個(gè)函數(shù)
---因?yàn)镸FC自己編寫了WinMain函數(shù),如果程序中再定義就重復(fù)定義了,
---而MFC編寫的的WinMain函數(shù)則調(diào)用了CWinApp::InitInstance函數(shù),所以InitInstance看起來似乎就是MFC程序的入口點(diǎn)。

---除了CWinApp成員函數(shù)外,Microsoft基類庫提供了以下全局函數(shù)
---來訪問CWinApp對象和其它全局信息
---1.AfxGetApp:獲取一個(gè)指向CWinApp對象的指針
---2.AfxGetInstanceHandle獲取當(dāng)前應(yīng)用程序?qū)嵗?/strong>(exe)的句柄
---3.AfxGetResourceHandle獲取應(yīng)用程序資源(dll)的句柄
---4.AfxGetAppName獲取指向包含應(yīng)用程序名稱的字符串指針
#總結(jié)
---MFC的核心:基于CWinApp的應(yīng)用程序對象(只能1個(gè))
---CWinApp的派生對象:代表1個(gè)程序本體(和程序本身有關(guān),和窗口無關(guān)的數(shù)據(jù)和動(dòng)作)
#CFrameWnd類

---提供了Windows單文檔界面(SDI),重疊或者彈出框架窗口的功能,以及用于管理窗口的成員
---要為應(yīng)用程序創(chuàng)建窗口,從FrameWnd派生類向派生類添加成員變量,以存儲(chǔ)特定的應(yīng)用程序數(shù)據(jù)
---在派生類中實(shí)現(xiàn):消息處理程序成員和消息映射(窗口函數(shù))

#創(chuàng)建和初始化Windows框架窗口
----CFrameWnd::Create(相當(dāng)于CreateWindow)
---這里可以只填寫LPCTSTR lpszClassName、LPCTSTR lpszWindowName兩個(gè)參數(shù),其它參數(shù)都存在默認(rèn)值
---如果LPCTSTR lpszClassName=NULL,則以MFC內(nèi)建窗口類產(chǎn)生一個(gè)標(biāo)準(zhǔn)的外框窗口
#創(chuàng)建一個(gè)CFrameWnd

#總結(jié)
---1,基于MFC的窗口程序,必須也只有1個(gè)CWinApp對象
---2.必須覆蓋CWinApp的虛函數(shù)InitInstance,在里面創(chuàng)建窗口,并把窗口對象保存在InitInstance()函數(shù)的成員變量m_pMainWnd里
---3.創(chuàng)建窗口是通過CFrameWnd對象,在它的構(gòu)造函數(shù)里面調(diào)用成員函數(shù)Create
#MFC程序的注意事項(xiàng)
---1.使用Win32Application創(chuàng)建工程(空項(xiàng)目)

---New一個(gè)Hello.cpp和Hello.h

---2.使用靜態(tài)鏈接庫

---3.在Hello.h中寫下防止頭文件被重復(fù)引用,防止被重復(fù)編譯的宏:
---ifndef 它是if not define的簡寫,是宏定義的一種:條件編譯
---#ifndef可以避免以下錯(cuò)誤:如果在.h文件中定義了全局變量,一個(gè)C文件包含了.h文件多次
---如果不加#ifndef宏定義,會(huì)出現(xiàn)變量重復(fù)定義的錯(cuò)誤;如果加了#ifndef則不會(huì)出現(xiàn)這種錯(cuò)誤.
---4.使用頭文件afxwin.h
#Hello.h的代碼
---Hello.cpp的代碼:
---m_pMainWnd相當(dāng)于窗口句柄,InitInstance相當(dāng)于WinMain函數(shù)
---效果如下

---在UpdateShow()下斷點(diǎn),F(xiàn)5調(diào)試,然后單步步入F11
---發(fā)現(xiàn)AfxWinMain就是以前的WinMain(),因此:InitInstance就是MFC對于WinMain()的封裝
---而且,在AfxWinMain內(nèi)部,調(diào)用了CWinApp的InitInstance()成員函數(shù)
#存在的問題
---1.WinMain在哪里? 2.消息循環(huán)在哪里? 3.窗口過程函數(shù)在哪里?
---關(guān)于作業(yè):創(chuàng)建窗口,右側(cè)帶滾動(dòng)條,大小300*300
---可以看到結(jié)構(gòu)體RECT是4個(gè)長整型的結(jié)構(gòu)體
第三:MFC的初始化過程1

#全局變量、全局對象總是在任何其它代碼之前執(zhí)行
---發(fā)現(xiàn)在main函數(shù)之前,a已經(jīng)被賦值

---全局對象也是一樣
---發(fā)現(xiàn)構(gòu)造函數(shù)早于main函數(shù)執(zhí)行

#總結(jié)
---1.全局對象的構(gòu)建,會(huì)早于程序的入口點(diǎn)
---2.而WinMain又廣泛使用了應(yīng)用程序?qū)ο螅–WinApp)的InitInstance()方法
---3.所以CWinApp被構(gòu)建成了全局對象
#模擬MFC的初始化
---CWinApp類的繼承關(guān)系:CObject > CCmdTarget > CWinThread > CWinApp
---CFrameWnd類的繼承關(guān)系: CObject > CCmdTarget > CWND > CFrameWnd
---編寫一個(gè)控制臺(tái)程序模擬MFC初始化
---注意:可以通過新建類來指定繼承關(guān)系

---先創(chuàng)建CObject.h
---再創(chuàng)建CObject.cpp
---考慮到要使用cout,創(chuàng)建一個(gè)public.h
---再創(chuàng)建CCmdTarget類,并且繼承CObject類
---聲明CCmdTarget的函數(shù)
---以此類推,分別創(chuàng)建CWinThread 和 CWinApp類
---在Mian.cpp中創(chuàng)建全局對象CWinApp
---發(fā)現(xiàn)逐級調(diào)用了父類的構(gòu)造函數(shù)和析構(gòu)函數(shù)
---CObject > CCmdTarget > CWND > CFrameWnd也是同理

第四:MFC的初始化過程2
#知識點(diǎn)
---MFC如何使用應(yīng)用程序?qū)ο?/p>
---CWinApp的2個(gè)可以覆蓋的虛函數(shù):
---從創(chuàng)建MFC窗口的頭文件可以看出
---CMyApp繼承CWinApp對象,而CMainWindow繼承CFrameWnd對象
---對象的繼承圖

---依次創(chuàng)建對象的構(gòu)造函數(shù)和析構(gòu)函數(shù)
---在mian()中創(chuàng)建全局對象
---注意:這里只是模擬,WinMain()是由系統(tǒng)調(diào)用,和平時(shí)程序調(diào)用存在本質(zhì)區(qū)別
---WinMain()沒有封裝在CWinApp類或者其它MFC的類中,因此WinMain()不是其它類的成員
---最后會(huì)調(diào)用AfxWinMain函數(shù)
---WinMain()的本質(zhì)是AfxWinMain函數(shù)
---執(zhí)行結(jié)果如下

--但是,相當(dāng)于MFC的程序,還缺少
---1.InitInstance()的虛函數(shù)
---2.m_pMainWnd的指針(指向CFrameWnd::CMainWindow對象),而m_pMainWnd指針是和InitInstance()同一級或者更上一級的類的成員變量
---查看InitInstance()的類:這里有3個(gè)類,但是最大的還是CWinThread類

---同樣的m_pMainWnd是CWinThread()的成員

---在CWinThread.h中定義純虛函數(shù)InitInstance()和m_pMainWnd指針
---注意包含WND.h
---在CMyApp.h中重定義虛函數(shù),
---在CMyApp.cpp中重寫虛函數(shù),并且new一個(gè)CMainWindow()對象(注意包含)
----在CMainWindow類里面重新定義一個(gè)方法
---然后在MainWindow.cpp中打印Create,并且在構(gòu)造函數(shù)時(shí)調(diào)用Create
---修改主函數(shù),在WinMain()里面調(diào)用
---1.WinMain()之前,全局對象的初始化會(huì)調(diào)用:
CObject > CCmdTarget > CWinThread > CWinApp > CMyApp
---2.進(jìn)入WinMain()之后,創(chuàng)建CMainWindow對象
CObject > CCmdTarget > CWnd > CFrameWnd > CMainWindow > Create
---3.最后調(diào)用的析構(gòu)函數(shù)(奇怪:CMainWindow系列的父類的析構(gòu)函數(shù)沒有調(diào)用)

第五:MFC運(yùn)行時(shí)類型識別:
#什么是RTTI(Runtime Type Information)
---運(yùn)行時(shí)類型信息程序,能夠使用父類的指針或者引用,來檢查這些指針或者引用所指向的對象的實(shí)際派生類
---幫助我們在實(shí)際程序運(yùn)行時(shí),判斷某個(gè)對象是否屬于某個(gè)類

---在項(xiàng)目設(shè)置里面選擇C++語言,勾選允許RTTI
---RTTI需要包含頭文件<typeinfo.h>

---typeid運(yùn)算符用來獲取一個(gè)表達(dá)式的類型信息,
---typeid 會(huì)把獲取到的類型信息保存到一個(gè) type_info 類型的對象里面,
---并返回該對象的常引用;當(dāng)需要具體的類型信息時(shí),可以通過成員函數(shù)來提取
#static關(guān)鍵字
---相當(dāng)于一個(gè)全局變量,獨(dú)立于該類的任何對象(和類關(guān)聯(lián),但是和類的任何對象不關(guān)聯(lián))
---不能在類的構(gòu)造函數(shù)中聲明(初始化),可以不需要?jiǎng)?chuàng)建對象,就使用類的static成員變量/方法
#const關(guān)鍵字
---定義一個(gè)只能初始化一次(不可以改變)的變量
---如果是類里面const,直接在構(gòu)造函數(shù)進(jìn)行聲明
---如果是static const,就在類外面進(jìn)行聲明
---在之前的HelloMFC項(xiàng)目中,在Hello.h中,加入宏:DECLARE_DYNAMIC,在Hello.cpp中加入宏:IMPLEMENT_DYNAMIC
---和typeid類似,MFC也存在IsKindOf()函數(shù)來檢測:
---(1)對象是否屬于指定的類,(2)對象是否屬于指定類派生的類
---在Hello.h中
---在Hello.cpp中:
---下斷點(diǎn)發(fā)現(xiàn)i=1,說明CWinApp是CMyApp的父類(父子類關(guān)系)
---查看DECLARE_DYNAMIC()的宏定義
---定義了一個(gè)靜態(tài)常量的CRuntimeClass結(jié)構(gòu)體,名為class##class_name
---定義了一個(gè)返回CRuntimeClass指針的GetRuntimeClass()的虛函數(shù),并且該函數(shù)的成員方法/變量都是常量(無法更改)
---AFX_DATA 實(shí)際是定義為空的,也許在MFC內(nèi)部編譯里有用,可以別管它
---##表示連接符,可以進(jìn)行字符的拼接,如class##CMyApp就是classCMyApp
---#表示字符串化,如#CMyApp就是"CMyApp"
----CRuntimeClass:類型記錄鏈表結(jié)構(gòu):
---對于MFC中每個(gè)CObject派生類來說,都有一個(gè)相關(guān)的CRuntimeClass結(jié)構(gòu)體,在程序運(yùn)行時(shí)可以訪問該結(jié)構(gòu)體來獲取對象及其基類的運(yùn)行時(shí)信息。
---在運(yùn)行時(shí)確定一個(gè)對象的類型是很重要的,尤其是在做類型檢查時(shí);而C++語言本身并不支持運(yùn)行時(shí)類信息
---每一個(gè)類擁有這樣一個(gè)CRuntimeClass成員變量,并且有一定的命名規(guī)則(在類名稱之前冠以“class”作為它的名稱)
---然后通過某種手段將整個(gè)類庫構(gòu)建好,“類別型錄網(wǎng)”能呈現(xiàn)類似的風(fēng)貌

---這里的class_name就是CMyApp
---1.定義一個(gè)classCMyApp的CRuntimeClass結(jié)構(gòu)體的靜態(tài)常量
---2.定義一個(gè)獲取當(dāng)前類的CRuntimeClass結(jié)構(gòu)體地址的指針
---在Hello.cpp中查看聲明classCMyApp和重寫虛函數(shù)的宏:
IMPLEMENT_DYNAMIC(CMyApp,CWinApp)
---這里是另外一個(gè)宏:
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
---跟進(jìn)去查看這個(gè)宏
---AFX_COMDAT是一種描述,用來控制編譯用的,可以去掉
---將這個(gè)宏轉(zhuǎn)移到Hello.cpp中
---這里的RUNTIME_CLASS也是一個(gè)宏目的是獲取指定類的CRuntimeClass地址
---替換之后的Hello.cpp
#總結(jié)
---在頭文件中定義當(dāng)前類的CRuntimeClass靜態(tài)常量結(jié)構(gòu)體classCMyApp,并且定義獲取當(dāng)前類的CRuntimeClass指針的虛函數(shù)GetRuntimeClass()
---初始化classCMyApp的值(指明類名、父類CRuntimeClass指針等)
---重寫GetRuntimeClass()虛函數(shù), 返回當(dāng)前CRuntimeClass結(jié)構(gòu)體的指針
#在int i = IsKindOf(((CRuntimeClass*)(&CWinApp::classCWinApp)));下斷點(diǎn)單步調(diào)試
---這里會(huì)刪除以下為了防止報(bào)錯(cuò)的代碼
---注意:這里pClass是父類的CRuntimeClass指針
---pClassThis是自己當(dāng)前類的CRuntimeClass指針
---this是當(dāng)前類CMyApp的指針

---進(jìn)入IsDerivedFrom函數(shù),這里的pBaseClass就是父類CWinApp的CRuntimeClass結(jié)構(gòu)體地址
---這里的pClassThis就是當(dāng)前類結(jié)構(gòu)體的CRuntimeClass地址
---進(jìn)入while循環(huán):如果當(dāng)前類的this(當(dāng)前類的CRuntimeClass地址)地址,不等于指定對象的CRuntimeClass地址
---就在當(dāng)前類的CRuntimeClass基礎(chǔ)上,向上遍歷父類的CRuntimeClass地址,直到父類遍歷完,m_pBaseClass指向NULL時(shí)退出循環(huán)(成功返回TRUE)

#作業(yè):讓CMianWindow支持RTTI,自己寫函數(shù)打印父類的CRuntimeClass
---頭文件
---遇到幾個(gè)問題:1.MFC里面不能使用printf函數(shù)
---2.想用m_pMainWnd->GetBaseRuntimeClass()調(diào)用,發(fā)現(xiàn)失?。ú恢朗莄onst限制還是我不能使用virtual,沒有辦法只能在構(gòu)造函數(shù)里面調(diào)用)
---3.這里調(diào)用CRuntimeClass指針的時(shí)候,直接pClassThis->m_pBaseClass,VC提示的成員變量是基于動(dòng)態(tài)鏈接庫的,我們這里是靜態(tài)鏈接庫
---4.如果要輸出LPCSTR,需要使用cout和for循環(huán),或者printf("%s")
第六:MFC六大核心機(jī)制:動(dòng)態(tài)創(chuàng)建
#什么是動(dòng)態(tài)創(chuàng)建
---MFC的動(dòng)態(tài)創(chuàng)建和C++的new幾乎沒有區(qū)別,但是回避了C++不讓如下語句的缺點(diǎn)
---編譯器不知道className是一個(gè)變量名,而不是類名,從className里面構(gòu)造的對象可能是錯(cuò)誤的
#面對對象之永久保存
---把內(nèi)存里面的東西,寫入到文件里面
---MFC永久保存:數(shù)據(jù)存儲(chǔ)入文件,讀出之后,根據(jù)文件的記錄,new一個(gè)對象

#類型記錄鏈表結(jié)構(gòu)(CRuntimeClass)
---在CFrameWnd中,存在宏:DECLARE_DYNCREATE

---在Hello.h中
---在Hello.cpp中:這里的邏輯是:變量class_name,根據(jù)class_name獲取到該類的結(jié)構(gòu)體CRuntimeclass,然后根據(jù)CRuntimeClass里面的CreateObject()創(chuàng)建該類的對象
---在MFC里面,根據(jù)宏,來創(chuàng)建當(dāng)前類或者父類的對象(前提是該類存在CRuntimeclass結(jié)構(gòu)體)
---查看DECLARE_DYNCREATE(CMainWindow)
---本質(zhì)上是上一節(jié)的DECLARE_DYNAMIC
---PASCAL 就是_stdcall調(diào)用約定,在DECLARE_DYNAMIC的 基礎(chǔ)上,創(chuàng)建了一個(gè)靜態(tài)函數(shù),并且返回CObject*
---查看IMPLEMENT_DYNCREATE
---查看IMPLEMENT_RUNTIMECLASS宏(這個(gè)就是IMPLEMENT_DYNMIC)
---因此,在Hello.h中
---在Hello,cpp中
#總結(jié)

第七:MFC的六大核心機(jī)制:消息映射
#MFC的消息映射
---消息映射是MFC的內(nèi)建的一個(gè)消息分配機(jī)制,利用數(shù)個(gè)宏和固定形式的寫法
---類似于填表格,就可以讓框架知道,一旦消息產(chǎn)生
---該往哪一個(gè)類進(jìn)行傳遞,每一個(gè)類只能擁有一個(gè)消息映射表(也可以沒有)

#Win32的消息機(jī)制
---1.創(chuàng)建窗口對象,然后初始化窗口對象的背景、類名、句柄、窗口程序(結(jié)合WinMain)
---2.將窗口類和OS關(guān)聯(lián)(鼠標(biāo)操作,OS傳遞消息給線程)
---3.線程(WinMain)獲取消息(GetMessage),然后翻譯(TranslateMessage)消息為虛擬碼(類似ASCII)才能被識別
---4.分發(fā)(DispachMessage)消息給OS,OS根據(jù)msg結(jié)構(gòu)中的hWnd窗口句柄,找到相應(yīng)的窗口類,然后根據(jù)注冊窗口時(shí)wndclass類結(jié)構(gòu)找到相應(yīng)的窗口函數(shù)WndPorc()

---相關(guān)的Win32代碼
#MFC的消息處理
---在Hello.h定義消息的處理函數(shù)
---在Win32里面是每個(gè)窗口設(shè)置消息處理函數(shù),而在MFC中,在類中定義的消息和對應(yīng)的消息處理函數(shù)
---在Hello,cpp中,在固定的宏里面,實(shí)現(xiàn)消息類型,然后根據(jù)消息類型設(shè)定消息處理函數(shù)
---發(fā)現(xiàn)打印了內(nèi)容,并且彈出了消息盒子

---在CMainWindow::OnPaint()中,需要明確設(shè)備對象、設(shè)備上下文、圖形對象

---畫圖的代碼如下:設(shè)備對象就是窗口(窗口句柄為空就是桌面),根據(jù)窗口句柄獲取設(shè)備上下文
---自定義圖形對象,然后和設(shè)備上下文關(guān)聯(lián),然后通過設(shè)備上下文進(jìn)行畫圖
---繪制圖形如下(這個(gè)只能顯示一次,而窗口是不停的繪制、渲染)
---這也是為什么上面MFC要利用dc對象來進(jìn)行畫圖

---查看聲明DeCLARE_MESSAGE_MAP()
---定義了2個(gè)靜態(tài)常量,聲明2個(gè)結(jié)構(gòu)體AFX_MSGMAP_ENTRY和AFX_MSGMAP
---虛函數(shù):GetMessageMap(),獲取messageMap的地址
---AFX_MSGMAP_ENTRY
----AFX_MSGMAP(查看靜態(tài)鏈接庫)
---查看BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()
---查看ON_WM_LBUTTONDOWN():根據(jù)消息類型,指定消息響應(yīng)函數(shù)
---查看AfxSig_vwp(指明了消息響應(yīng)函數(shù)的:返回類型、參數(shù)列表)
---查看ON_WM_PAINT()
---匯總起來,在Hello.h中
---private是完全私有的,只有當(dāng)前類中的成員能訪問到;protected是受保護(hù)的,只有當(dāng)前類的成員與繼承該類的類才能訪問
---聲明了2個(gè)結(jié)構(gòu)體AFX_MSGMAP_ENTRY和AFX_MSGMAP,以及獲取AFX_MSGMAP結(jié)構(gòu)體地址的虛函數(shù)
---注意:afx_msg就是消息響應(yīng)函數(shù)的返回類型
---Hello.cpp
---每一個(gè)類都有一個(gè)私有的AFX_MSGMAP_ENTRY結(jié)構(gòu)體,里面存儲(chǔ)著:不同類型的消息所對應(yīng)的消息處理函數(shù)、消息ID等
---然后每一個(gè)類存在一個(gè)保護(hù)的類型的結(jié)構(gòu)體AFX_MSGMAP,這個(gè)結(jié)構(gòu)體2個(gè)指針分別指向父類的AFX_MSGMAP,和自己的AFX_MSGMAP_ENTRY
---因此,我們可以通過AFX_MSGMAP獲取自己和父類的AFX_MSGMAP_ENTRY
---AFX_MSGMAP_ENTRY結(jié)構(gòu)體的類型是{{消息結(jié)構(gòu)體1},{消息結(jié)構(gòu)體2},{空消息結(jié)構(gòu)體}}
---注意:消息結(jié)構(gòu)體里面的消息響應(yīng)函數(shù)的參數(shù)、返回類型和命名是固定的
#MFC的三大類消息

#MFC處理消息的原理
---MFC內(nèi)部存在一個(gè)窗口過程處理函數(shù)
---根據(jù)MessageMap鏈表找到對應(yīng)的消息函數(shù)AFX_MSGMAP_ENTRY
---MessageMap存在2個(gè)指針,pBaseMap指向父類的AFX_MSGMAP結(jié)構(gòu)體,最終指向CCmdTarget::messageMap
---lpEntries指向自己的AFX_MSGMAP_ENTRY結(jié)構(gòu)體,在結(jié)構(gòu)體內(nèi)存在不同消息的:消息ID、消息響應(yīng)函數(shù)等
---因此:一個(gè)支持消息映射的類(標(biāo)準(zhǔn)消息),必須繼承CCmdTarget
#作業(yè)
---新建一個(gè)類,繼承于CMainWind(CNewWnd),給CMainWind添加鼠標(biāo)左鍵點(diǎn)擊事件,創(chuàng)建新的CNewWnd窗口,新窗口添加鼠標(biāo)左鍵點(diǎn)擊事件,彈出MessageBox
---在Hello.h中,聲明新建的類CNewWnd,并且聲明消息映射和消息響應(yīng)函數(shù)
---在hello.cpp中定義CNewWnd的指針,來進(jìn)行新窗口的創(chuàng)建
---創(chuàng)建效果如下

第八:MFC六大核心機(jī)制:命令的傳遞
#MFC命令傳遞
---消息會(huì)按照規(guī)定的路線,游走于各個(gè)對象之間,直到找到它的消息處理函數(shù)
---如果找不到,就交給DefWindowPro函數(shù)處理

---在Create(NULL,"主窗口")下斷點(diǎn)
---單步進(jìn)入CreateEx
---CREATESTRUCT結(jié)構(gòu)體用來存儲(chǔ)創(chuàng)建窗口的參數(shù)
---單步步入預(yù)窗口創(chuàng)建:PreCreateWindow(cs),設(shè)置窗口的風(fēng)格,并且注冊(根據(jù)所屬的類來進(jìn)行定義窗口的風(fēng)格)
---單步步入AfxDeferRegisterClass
---AfxHookWindowCreate(this)函數(shù)分析(this是CWnd類地址)
---_AfxCbtFilterHook函數(shù)分析
---在D:\VC6.0\VC98\MFC\SRC路徑下WINCORE.CPP文件

---在里面將Win32里面的窗口過程處理函數(shù)替換成afxWndProc

#總結(jié)MFC創(chuàng)建窗口流程
---1.判斷創(chuàng)建的窗口是否存在菜單,如果存在則加載菜單
---2.獲取窗口類的類名,根據(jù)類名調(diào)用CreateEx()函數(shù)
---3.聲明CREATESTRUCT結(jié)構(gòu)體cs,用來存儲(chǔ)Win32中CreateWindow()的參數(shù)
---4.調(diào)用PreCreateWindow(cs)函數(shù),判斷窗口類名是否為空(是否默認(rèn)的窗口對象)
---5.如果是默認(rèn)的窗口類,就調(diào)用AfxDeferRegisterClass函數(shù),初始化窗口類,根據(jù)不同的類來設(shè)置窗口的風(fēng)格、背景和如果沒有消息響應(yīng)的默認(rèn)窗口過程函數(shù),最后調(diào)用AfxRegisterClass()注冊窗口類
---6.在調(diào)用CreateWindowEx創(chuàng)建窗口之前,先調(diào)用AfxHookWindowCreate(this)下鉤子,也就是說,在DispatchMessage之后,AfxHookWindowCreate(this)最先受到消息
---7.通過SetWindowsHookEx安裝WH_CBT的過濾函數(shù),Windows系統(tǒng)在進(jìn)行窗口操作(最大化、最小化窗口)執(zhí)行之前,調(diào)用這個(gè)_AfxCbtFilterHook過濾函數(shù)
---8._AfxCbtFilterHook將Win32里面的窗口過程處理函數(shù)替換成afxWndProc

---在上一節(jié)的作業(yè)的消息響應(yīng)函數(shù)這里下一個(gè)斷點(diǎn)

---首先進(jìn)入的是消息響應(yīng)函數(shù)的調(diào)用格式
---WindowProc中,調(diào)用OnWndMsg()對消息進(jìn)行處理
---如果處理失敗,調(diào)用DefWindowProc()對消息進(jìn)行默認(rèn)處理
---執(zhí)行完WindowProc之后,進(jìn)入AfxCallWndProc()的
---執(zhí)行完AfxCallWndProc()之后,進(jìn)入AfxWndProc(真正的窗口過程處理函數(shù))
---DispatchMessage之后,AfxWndProc最先接受到消息
---本質(zhì)是調(diào)用AfxCallWndProc函數(shù),把消息送給CWnd類或其派生類的對象。
---該函數(shù)主要是把消息和消息參數(shù)(nMsg、wParam、lParam)傳遞給MFC窗口對象的成員函數(shù)WindowProc(pWnd->WindowProc)作進(jìn)一步處理。
---如果是WM_INITDIALOG消息,則在調(diào)用WindowProc前后要作一些處理。
#總結(jié)MFC的窗口過程(消息響應(yīng)函數(shù))
---每一個(gè)“窗口類”都有自己的窗口過程,使用該“窗口類”創(chuàng)建的窗口都使用它的窗口過程
---MFC的窗口對象在創(chuàng)建窗口時(shí),也使用已經(jīng)注冊的“窗口類”(使用應(yīng)用程序提供的窗口過程),AfxWndProc或AfxWndProcBase(動(dòng)態(tài)鏈接庫)(本質(zhì)是AfxCallWndProc)
---窗口創(chuàng)建最終是通過調(diào)用CWnd::CreateEx函數(shù)完成的(根據(jù)窗口類名調(diào)用),CreateEx函數(shù)流程如下
---在創(chuàng)建窗口之前,創(chuàng)建了一個(gè)WH_CBT類型的鉤子(Hook)。這樣,創(chuàng)建窗口時(shí)所有的消息都會(huì)被鉤子過程函數(shù)_AfxCbtFilterHook截獲。
---AfxCbtFilterHook函數(shù)首先檢查是不是希望處理的 Hook──HCBT_CREATEWND。如果是,則先把MFC窗口對象和剛剛創(chuàng)建的Windows窗口對象捆綁在一起,建立它們之間的映射(見后面模塊-線程狀態(tài));
---然后,調(diào)用::SetWindowLong設(shè)置窗口過程為AfxWndProc,并保存原窗口過程在窗口類成員變量m_pfnSuper中,這樣形成一個(gè)窗口過程鏈。需要的時(shí)候,原窗口過程地址可以通過窗口類成員函數(shù)GetSuperWndProcAddr得到。
---AfxWndProc就成為CWnd或其派生類的窗口過程。不論隊(duì)列消息,還是非隊(duì)列消息,都送到AfxWndProc窗口過程來處理
---Windows消息送給AfxWndProc窗口過程之后,AfxWndProc得到HWND窗口對應(yīng)的MFC窗口對象
---然后,搜索該MFC窗口對象和其基類的消息映射數(shù)組,判定它們是否處理當(dāng)前消息,如果是則調(diào)用對應(yīng)的消息處理函數(shù),否則,進(jìn)行缺省處理

#MFC中每一個(gè)窗口,都對應(yīng)了一個(gè)窗口類
---在這個(gè)窗口產(chǎn)生的消息,CWnd或者派生類的對象調(diào)用OnWndMsg搜索本對象或者基類的消息映射數(shù)組messageMap,尋找當(dāng)前消息的消息處理函數(shù)(在_messageEntries數(shù)組里面)

---在OnWndMsg搜索本對象對應(yīng)的消息映射數(shù)組,找到對應(yīng)的消息響應(yīng)函數(shù)
#總結(jié)
---在MFC中,每一個(gè)窗口都對應(yīng)一個(gè)MFC類(繼承于CCmdTarget)
---通過CWinApp的派生類,重寫InitInstance方法(相當(dāng)于WinMain入口)
---在InitInstance中,創(chuàng)建CFrameWnd對象(子類也可以),進(jìn)而調(diào)用CFrameWnd的構(gòu)造函數(shù)
---在構(gòu)造函數(shù)中,調(diào)用Create()創(chuàng)建MFC窗口(區(qū)別于CreateWindow),本質(zhì)是調(diào)用CreateEx
---CreateEx通過結(jié)構(gòu)體CREATESTRUCT傳遞定義窗口類的參數(shù),然后調(diào)用PreCreateWindow(cs)進(jìn)行窗口對象的自定義,以及窗口類的注冊
---調(diào)用AfxHookWindowCreate(this)在窗口創(chuàng)建前,創(chuàng)建了一個(gè)WH_CBT類型的鉤子(Hook),所有的消息都會(huì)被鉤子過程函數(shù)_AfxCbtFilterHook截獲
---_AfxCbtFilterHook,則先把MFC窗口對象(該對象必須已經(jīng)創(chuàng)建了)和剛剛創(chuàng)建的Windows窗口對象捆綁在一起建立它們之間的映射(將窗口類::WindowProc的消息處理過程和MFC窗口進(jìn)行關(guān)聯(lián))
---然后調(diào)用::SetWindowLong設(shè)置將Win32里面的窗口過程為AfxWndProc(本質(zhì)是AfxCallWndProc)
---AfxWndProc就成為CWnd或其派生類的窗口過程。而經(jīng)過消息分發(fā)之后沒有被處理的消息,將送給原窗口過程處理。
---Windows消息送給AfxWndProc窗口過程之后(Win32是OS直接DispatchMessage給窗口句柄),AfxWndProc得到HWND窗口對應(yīng)的MFC窗口對象
---在MFC類的窗口過程函數(shù)中AfxWndProc,調(diào)用OnWndMsg()對消息進(jìn)行過濾識別,如果是標(biāo)準(zhǔn)消息,就搜索該MFC窗口對象和其基類的消息映射數(shù)組,判定它們是否處理當(dāng)前消息,如果是則調(diào)用對應(yīng)的消息處理函數(shù),否則,進(jìn)行缺省處理
#再次分析窗口過程處理函數(shù)AfxWndProc的本質(zhì)AfxCallWndProc(注意:這個(gè)順序應(yīng)該是反著來)
---步驟3:注意每一個(gè)CWnd對象的派生類(MFC窗口對象)都存在函數(shù)
---序員可以在CWnd的派生類中覆蓋它,改變MFC分發(fā)消息的方式。例如,MFC的CControlBar就覆蓋了WindowProc,對某些消息作了自己的特別處理,其他消息處理由基類的WindowProc函數(shù)完成
---在當(dāng)前例子中,當(dāng)前對象的類CTview沒有覆蓋該函數(shù),所以CWnd的WindowProc被調(diào)用。---這個(gè)函數(shù)把下一步的工作交給OnWndMsg函數(shù)來處理。如果OnWndMsg沒有處理,則交給DefWindowProc來處理
---OnWndMsg和DefWindowProc都是CWnd類的虛擬函數(shù)。
---步驟2
---步驟1:
#總結(jié)
---DispatchMessage之后,MFC窗口對象::AfxWndProc接受到消息,調(diào)用?AfxCallWndProc
---AfxCallWndProc根據(jù)接到的消息,調(diào)用消息處理函數(shù)CWnd::WindowProc
---WindowProc函數(shù)調(diào)用OnWndMsg遍歷父子類的messageMap結(jié)構(gòu)體,找到對應(yīng)消息的響應(yīng)過程函數(shù)
#為什么要使用消息映射

#MFC消息傳遞總結(jié)
