【滴水基礎(chǔ)】5.MFC(3)
第十:GDI基礎(chǔ)概念和MFC的CDC類
#GDI圖形設(shè)備(Graphics Device InterFace)
---和Win32Api類似,為應(yīng)用程序提供了可調(diào)用的多種服務(wù),這些服務(wù)構(gòu)成了強大、通用的圖形編程語言
#DC圖形描述表(同時表示設(shè)備上下文)
---Windows程序在屏幕、打印機、其它輸出設(shè)備上畫圖時,并不是將像素直接輸出到設(shè)備上
---而是將圖先繪制到設(shè)備描述表(內(nèi)存設(shè)備dc)表示的邏輯意義上的顯示平面上去
---它是Windows中的一種數(shù)據(jù)結(jié)構(gòu),包含了GDI需要的所有有關(guān)于顯示平面情況的描述
---早期的圖形開發(fā),是針對具體的設(shè)備開發(fā)(如:根據(jù)打印機廠家提供的不同接口開發(fā)不同的代碼),需要適配不同的顯卡,造成開發(fā)者困難
---Windows通過驅(qū)動管理,將設(shè)備接口細節(jié)隱藏在OS內(nèi)部,我們只需要針對一個:公有的虛擬設(shè)備DC
#MFC不同場景下的DC類
---MFC的CDC類,將DC和HDC(句柄:可以操作所有DC有關(guān)的GDI函數(shù))的GDI函數(shù)封裝到了一起,派生了四個不同場景的DC類,以下是常用的3個
---注意:OnPaint是ON_WM_PAINT消息的響應(yīng)函數(shù),當(dāng)窗口進行某些特定操作引起窗口重繪時,會發(fā)送WM_PAINT消息
---系統(tǒng)會在多個不同的時機發(fā)送WM_PAINT消息:當(dāng)?shù)谝淮蝿?chuàng)建一個窗口時,當(dāng)改變窗口的大小時,當(dāng)把窗口從另一個窗口背后移出時,當(dāng)最大化或最小化窗口時等等
---大多數(shù)的時候應(yīng)用也需要能夠主動引發(fā)窗口中的繪制操作,比如當(dāng)窗口顯示的數(shù)據(jù)改變的時候,這一般是通過InvalidateRect和InvalidateRgn函數(shù)來完成的。
---ON_WM_PAINT可以捕獲此消息,并自己調(diào)用OnPaint()函數(shù)來實現(xiàn)對某些要素圖形的重新繪制

---當(dāng)然,視頻里面省略了一個

#在消息響應(yīng)函數(shù)的基礎(chǔ)上,區(qū)分Win32Api和MFC
---如果是傳統(tǒng)的Win32Api的調(diào)用,分為設(shè)備對象(窗口),設(shè)備上下文(內(nèi)存),圖形對象
---通過設(shè)備對象獲取窗口的設(shè)備上下文,然后自定義圖形對象
---最后,將圖形對象和設(shè)備上下文關(guān)聯(lián),調(diào)用WinApi進行繪圖
---繪制圖形如下(這個只能顯示一次,而窗口是不停的繪制、渲染)
---這也是為什么上面MFC要利用dc對象來進行畫圖

#MFC的窗口句柄
---哪如何獲得窗口類的自己的句柄呢?有如下方法:
---1、this->m_hWnd; ????????????//成員變量
---2、GetSafeHwnd(); ????????????//成員方法
---3、AfxGetMainWnd()->m_hWnd;
#在MFC里面模擬Win32Api的調(diào)用
---Hello.h
---hello.cpp:為什么OnPaint()里面調(diào)用:BeginPaint(m_hWnd,&ps)和EndPaint(m_hWnd,&ps)?
---BeginPaint函數(shù)的作用是告訴Windows系統(tǒng),要開始向顯示卡輸出內(nèi)容了,把這次顯示的操作請求放到系統(tǒng)顯示隊列里。
---由于系統(tǒng)上的顯示卡往往只有一個,那么這種資源是獨占的,所以操作系統(tǒng)會讓顯示操作線性化,保證每個窗口的顯示是獨立進行的,而不是A窗口顯示一部份,或者B窗口顯示一部份,而是A窗口顯示完成后再讓B窗口顯示
---因此,BeginPaint函數(shù)就是跟操作系統(tǒng)說,我需要顯示了,你安排好吧。當(dāng)BeginPaint返回時,就獲取到系統(tǒng)的顯示資源句柄,這樣就可以調(diào)GDI一大堆函數(shù)來操作了
---顯示完成后,一定要記得調(diào)用函數(shù)EndPaint,因為使用BeginPaint函數(shù)請求了獨占的顯示資源后,如果不釋放回去,就會讓其它程序永遠獲取不到顯示資源了,這樣系統(tǒng)就死鎖了
---應(yīng)用程序除了響應(yīng)WM_PAINT消息外,不應(yīng)該調(diào)用BeginPaint
---效果如下

---點擊鼠標(biāo)左鍵

---上面是Win32Api進行的操作,如果替換為MFC封裝的DC類
---效果如下

#設(shè)備描述表的屬性(重要)

---例如,修改字體的顏色
---效果如下

#鼠標(biāo)左鍵點擊,畫一個黃色的矩形
---pen(1,2,3)相當(dāng)于CreatePen(1,2,3)
---代碼如下
---效果如下

---總結(jié)

第十一:WindowsGDI

#GDI的映射模式
---映射模式是設(shè)備描述表的屬性,用于確定邏輯坐標(biāo)值(內(nèi)存)到設(shè)備坐標(biāo)值(顯示屏)的轉(zhuǎn)換(設(shè)備坐標(biāo)值:窗口中相對應(yīng)的像素位置)
---傳遞給CDC(CDC封裝了HDC和DC有關(guān)的函數(shù))輸出函數(shù)的邏輯坐標(biāo)值,然后被轉(zhuǎn)換為設(shè)備坐標(biāo)值
---通過CDC::SetMapMode來進行設(shè)置邏輯設(shè)備到設(shè)備坐標(biāo)的映射方式

#映射模式的nMapMode取值與含義
符號常量 ????????????????數(shù)字常量 ????????????x方向 ????????? y方向 ????????邏輯單位的大小
MM_TEXT ????????????????????1 ????????????向右(默認(rèn))向下(默認(rèn)) ??? ??????? 像素
MM_LOMETRIC ?????????? 2 ?????????????????? 向右 ????????? 向上 ????????????????????0.1 mm
MM_HIMETRIC ????????????3 ?????????????????? 向右 ????????? 向上 ????????????????????0.01 mm
MM_LOENGLISH ???????? 4 ?????????????????? 向右 ????????? 向上 ????????????????????0.01 in
MM_HIENGLISH ????????? 5 ?????????????????? 向右 ????????? 向上 ????????????????????0.001 in
MM_TWIPS ????????????????? 6 ????????????????????向右???????????? 向上 ????????????????1/1440 in ?
MM_ISOTROPIC ????????? 7 ????????????????????自定義 ????? 自定義 ????????????????????自定義
MM_ANISOTROPIC ???? 8 ????????????????????自定義 ????????自定義 ????????????????? 自定義

#設(shè)備空間下的坐標(biāo)
---1.客戶區(qū)坐標(biāo) 2.屏幕坐標(biāo) 3.全窗口坐標(biāo)

#GDI坐標(biāo)空間
---Windows程序利用坐標(biāo)空間和轉(zhuǎn)換來對圖形輸出進行縮放、旋轉(zhuǎn)、平移、斜切、反射等
---坐標(biāo)空間是一個平面的空間,通過互相垂直的XY軸來定位二維對象
#Win32Api使用4種坐標(biāo)空間

---在窗口的ON_WM_PAINT()消息的響應(yīng)函數(shù)OnPaint()中
---繪制圓形
---這里的映射模式是默認(rèn)的以像素為映射:MM_TEXT

---如果將映射模式設(shè)置為:MM_LOMETRIC(修改的是邏輯坐標(biāo)的單位)
---一個邏輯單位:0.1毫米,x和y軸方向:x+y-
---效果如下
---映射模式的修改優(yōu)點:與物理設(shè)備無關(guān)如顯示器,不同的屏幕具有不同的分辨率

---獲取客戶區(qū)的邏輯坐標(biāo),使得圓形鋪滿整個窗口
---圓形可以鋪滿整個窗口,但是一旦放大窗口,就會變成橢圓

---可以通過修改映射模式進行自適應(yīng),發(fā)現(xiàn)放大了之后,圓也不會變成橢圓
---將映射模式改為:MM_ANISOTROPIC,發(fā)現(xiàn)邏輯單位還是1個像素點,但是放大之后,就會變成橢圓
---通常情況下SetWindowExt()函數(shù)和SetViewportExt()函數(shù)成對調(diào)用(我這里在網(wǎng)上找了一個例子)
---在X軸方向,每個邏輯單位有 1024 / 10240 個像素,
---而在Y軸方向,每個邏輯單位有 768 / 7680 個像素
#總結(jié)

#對應(yīng)鼠標(biāo)左鍵點擊消息ON_WM_LBUTTONDOWN()對應(yīng)的響應(yīng)函數(shù):OnLButtonDown(UINT nFlags,CPoint point)
---這里就是在以鼠標(biāo)點擊的點為源點(設(shè)備坐標(biāo)),然后畫矩形
---效果如下

---我們設(shè)置映射模式,將原本的默認(rèn)的映射,改為:MM_LOMETRIC
---:一個邏輯單位=0.1毫米,x軸y軸方向:X+Y-
---效果如下:

#先設(shè)置CMianWindow主窗口的范圍(0,0,515,540)
---發(fā)現(xiàn)畫一個(0,0,500,500)的圓形正好在中間

---現(xiàn)在更改窗口的原點,改為邏輯坐標(biāo)的(100,100)

#窗口和視口的繪圖
---窗口中的坐標(biāo)都是邏輯坐標(biāo),視口中的坐標(biāo)都是設(shè)備坐標(biāo)。我們的繪圖語句(dc的成員函數(shù))中用的坐標(biāo)都是邏輯坐標(biāo)
---這里窗口的原點,變成了邏輯坐標(biāo)的(100,100),截取了一個小正方形,并且會補充一部分的空白
--而窗口到視口的映射方式?jīng)]有變化,所以顯示是部分的圓

---如果改為
---效果如下圓形直接顯示完全

---背后的原理如下:先創(chuàng)建窗口,然后在窗口中繪制(100,100,500,500)的圓形
---在邏輯坐標(biāo)畫好之后,截?。?00,100)為原點的部分

---更改視口
---效果如下:

---原理分析:將窗口的映射到視口上,由于在邏輯坐標(biāo)上,窗口是完整的,所以圓形不會缺失(大?。?00,500)
---但是視口上,只有(400,400)來接受映射會舍棄一部分,所以視口原點在原來的視口基礎(chǔ)上,變成了原來視口的坐標(biāo)位置的(100,100)開始
---總結(jié):
---要更改 窗口/視口 原點,就在原來 窗口/視口 坐標(biāo)系的基礎(chǔ)上,進行 窗口/視口 的原點定位
---然后進行投影,沒有投影到的部分,就是空白區(qū)域

#頁面控件到設(shè)備空間的轉(zhuǎn)換
---根據(jù)映射方式來確定繪圖操作單位大小的一種度量轉(zhuǎn)換

第十二:GDI繪圖

#常用的繪圖函數(shù)
---:MoveTo:在畫線前設(shè)定當(dāng)前的位置(指定起始)
---:LineTo:從當(dāng)前位置畫1條線到指定位置(指定指定終止)
---:Polyline:將一系列的點用線段連接起來(無需指定起始位置)
---:PolylineTo:從當(dāng)前位置開始,將一系列點用線段連接起來(需要指定起始位置)
---:Elipse:畫圓或者畫橢圓
---:Rectangle:畫一個帶直角的矩形
---:FillRect:用指定畫刷填充矩形
---:Draw3dRect:實現(xiàn)3D立體感
---效果如下

---也可以通過Polyline將一系列的點進行連接
---當(dāng)然也可以PolylineTo函數(shù),需要設(shè)定起始位置,但是和Polyline沒有本質(zhì)的區(qū)別
---效果如下

---系統(tǒng)中的畫刷、畫筆、字體等,可以自己創(chuàng)建并設(shè)計,也可以使用系統(tǒng)內(nèi)置的
---CreateStockObject表示使用系統(tǒng)內(nèi)置,LTGRAY_BRUSH是一個內(nèi)置宏
---Windows的系統(tǒng)預(yù)定義的畫刷如下
---CreateStockObject的優(yōu)點:無需用dc.SelectObject()去更換畫刷對象
---效果如下:

#如果鼠標(biāo)點擊在矩形內(nèi),就修改窗口名稱
---效果如下,點擊矩形內(nèi),窗口名改為:yes,點擊其它區(qū)域,窗口名:no

---如果想改變矩形邊框的顏色,風(fēng)格,可以使用pen
---對于畫筆、畫刷等圖形對象,在MFC中GDI的函數(shù)中可以使用:SelectObject()函數(shù)加載,并且返回了原來的圖形對象,可以在后面加載原來的圖形對象
---為什么要保存原來的圖形對象:因為GDI沒有異常機制,載入自定義的pen之后,在退出的時候,先調(diào)用的默認(rèn)pen的析構(gòu)函數(shù),再調(diào)用dc的析構(gòu)函數(shù)
---但是dc里面存在的是自定義的pen,而默認(rèn)的pen已經(jīng)優(yōu)先釋放了,所以會產(chǎn)生異常
---設(shè)置字體的大小,風(fēng)格
---效果如下

---更大范圍的更改字體:通過LOGFONT結(jié)構(gòu)體來進行更大范圍的字體的傳參
---效果如下:

---帶陰影的字體:
---效果如下

#作業(yè)
---用GDI繪制一個excel: Draw3dRect; FillRect; 填充之后畫像
---注意我的主窗口大小
---思路:先兩點連線,畫出表格
---然后自定義畫筆,畫刷,填充橫軸、縱軸的區(qū)域
---效果如下:

第十三:鼠標(biāo)消息
#Windows鼠標(biāo)消息
---Windows有20多種不同的消息,用來報告與鼠標(biāo)有關(guān)的輸入事件
---這些消息分為:客戶消息和非客戶消息,我們只需要關(guān)注客戶消息(知識點:理解客戶消息和非客戶消息)
#客戶區(qū)域的鼠標(biāo)消息
#按下鼠標(biāo)左鍵輸出鼠標(biāo)點擊的坐標(biāo)(注意:point是物理的設(shè)備坐標(biāo))
---效果如下:

#鼠標(biāo)移動并且按下鼠標(biāo)左鍵才能顯示坐標(biāo)
---定義的鼠標(biāo)消息
---定義消息映射
---定義移動鼠標(biāo)的消息響應(yīng)函數(shù)
---效果如下:需要同時按3個鍵盤

#窗口的非客戶區(qū)消息
---定義非客戶區(qū)域的消息映射
---重寫映射函數(shù)
---效果如下:只有在非客戶區(qū)域才能接受消息

---自定義部分非客戶區(qū)的消息處理
---只有點擊系統(tǒng)菜單,才改變窗口的名字
#窗口在接收到客戶、非客戶區(qū)消息之前,先接受到:光標(biāo)的屏幕坐標(biāo)(很重要)
---通過 ON_WM_NCHITTEST() 確定光標(biāo)在客戶區(qū) or 不在客戶區(qū)
---在BEGIN_MESSAGE_MAP中添加ON_WM_HITTEST(),
---然后添加頭文件,重寫相應(yīng)的響應(yīng)函數(shù)
---發(fā)現(xiàn)可以像非客戶區(qū)域一樣,拖動客戶區(qū)區(qū)域
#鼠標(biāo)位置畫直線(注意不能和上面的將客戶區(qū)改為非客戶區(qū)的一起,不然只能拖到窗口)
---注意,在兩個消息處理函數(shù)之間,需要通過全局的CPoint來傳遞鼠標(biāo)的位置
---然后通過OnLButtonDown來進行初始坐標(biāo)的傳輸
---通過SetCapture/GetCapture來確保鼠標(biāo)的位置被當(dāng)前窗口捕獲(即使在窗口之外)
---利用OnLButtonUp來進行繪圖操作
---效果如下

---作業(yè)1:鼠標(biāo)畫一條紅色的直線(思路:自定義畫筆)
---效果如下

---作業(yè)2:Ctrl+鼠標(biāo)左鍵畫一個矩形
---效果如下:特點是鼠標(biāo)在窗口外面,任然可以畫

---作業(yè)3:鼠標(biāo)右鍵畫一條黃色的線條(非直線)
---思路:捕獲鼠標(biāo)光標(biāo)滑動的地址,然后將這些點通過直線連接起來,從宏觀上來看就是曲線
---思路是這樣,但是好像原點都是按下的初始點
---但是我記得LineTo之后,原點是會改變的,不知道為啥不行

第十四:鍵盤消息
---通過給擁有輸入焦點的窗口發(fā)送:WM_KEYDOWN 和 WM_KEYUP 消息
---來報告按鈕是按下還是松開的事件,這些被稱為按擊鍵消息
---除了 ALT 和 F10 以外的所有按鍵都產(chǎn)生按下/抬起的消息
---而 Alt 和 F10 是系統(tǒng)鍵,對 Window 具有特殊的意義
#鍵盤消息
---在BEGIN_MESSAGE_MAP中,添加: ON_WM_KEYDOWN()
---部分的虛擬鍵代碼(VK_CANCEL代碼是一個虛擬鍵碼,它包括同時按下兩個鍵(Ctrl-Break))

---Backspace、Tab、Enter、Esc和空格——通常用于Windows程序。不過,Windows一般用字元訊息(而不是鍵盤訊息)來處理這些鍵
---Windows程序通常不需要監(jiān)視Shift、Ctrl或Alt鍵的狀態(tài)

---重寫消息響應(yīng)函數(shù)
---通過調(diào)試模式運行,發(fā)現(xiàn)Ctrl對應(yīng)的十進制代碼,以及之前的狀態(tài)都是抬起
---但是一直按卻沒有1的出現(xiàn)
---在DeBugView中一直按下Ctrl也沒有出現(xiàn)1,奇怪

#獲取區(qū)分大小寫的鍵盤消息,在消息映射中添加:ON_WM_CHAR()
---重寫映射函數(shù)
---效果如下:
# ON_WM_CHAR 和 ON_WM_KEYDOWN 的區(qū)別
---ON_WM_CHAR是通過TranslateMessage翻譯之后,轉(zhuǎn)換為ASCII碼
---不經(jīng)過翻譯是無法識別按鍵的大小寫的
---當(dāng)然,也可以判斷Shift鍵的狀態(tài)來判斷大小寫
---視頻里面'VK_A'不加''的話編譯不通過,但是加了''在調(diào)試的時候看不到輸出。不知道問題出在哪里?
#設(shè)置鍵盤消息的鉤子函數(shù):SetWindowsHookEx
#鉤子函數(shù)的定義
---鉤子(Hook),是Windows消息處理機制的一個平臺,應(yīng)用程序可以在上面設(shè)置子程以監(jiān)視指定窗口的某種消息,而且所監(jiān)視的窗口可以是其他進程所創(chuàng)建的。
---當(dāng)消息到達后,在目標(biāo)窗口處理函數(shù)之前處理它。鉤子機制允許應(yīng)用程序截獲處理window消息或特定事件。
----鉤子實際上是一個處理消息的程序段,通過系統(tǒng)調(diào)用,把它掛入系統(tǒng)。每當(dāng)特定的消息發(fā)出,在沒有到達目的窗口前,鉤子程序就先捕獲該消息,
---這時鉤子函數(shù)即可以加工處理(改變)該消息,也可以不作處理而繼續(xù)傳遞該消息,還可以強制結(jié)束消息的傳遞
#鉤子函數(shù)的原理
---每一個Hook都有一個關(guān)聯(lián)的指針列表,稱之為鉤子鏈表
---這個列表的指針指向鉤子的各個處理子程序
----當(dāng)與指定的Hook類型關(guān)聯(lián)的消息發(fā)生時,系統(tǒng)就把這個消息傳遞到Hook子程
---最近安裝的鉤子放在鏈的開始,而最早安裝的鉤子放在最后,后加入的先獲得控制權(quán)
---SetWindowsHookEx()函數(shù)的最后一個參數(shù)決定了此鉤子是系統(tǒng)鉤子還是線程鉤子
----線程鉤子用于監(jiān)視指定線程的事件消息,一般在當(dāng)前線程或者當(dāng)前線程派生線程內(nèi)---
---系統(tǒng)鉤子監(jiān)視系統(tǒng)中的所有線程的事件消息。因為系統(tǒng)鉤子會影響系統(tǒng)中所有的應(yīng)用程序,所以鉤子函數(shù)必須放在獨立的動態(tài)鏈接庫(DLL) 中
---系統(tǒng)自動將包含“鉤子回調(diào)函數(shù)”的DLL映射到受鉤子函數(shù)影響的所有進程的地址空間中,即將這個DLL注入了那些進程
---幾點說明:
(1)如果對于同一事件(如鼠標(biāo)消息)既安裝了線程鉤子又安裝了系統(tǒng)鉤子,那么系統(tǒng)會自動先調(diào)用線程鉤子,然后調(diào)用系統(tǒng)鉤子。
(2)對同一事件消息可安裝多個鉤子處理過程,這些鉤子處理過程形成了鉤子鏈。當(dāng)前鉤子處理結(jié)束后應(yīng)把鉤子信息傳遞給下一個鉤子函數(shù)。
(3)鉤子特別是系統(tǒng)鉤子會消耗消息處理時間,只有在必要的時候才安裝鉤子,在使用完畢后要及時卸載
#掛一個全局的鍵盤鉤子(系統(tǒng)鉤子)

---選擇靜態(tài)鏈接庫(VC6的版本太老了,只能選擇靜態(tài)鏈接庫)

---生產(chǎn)的靜態(tài)鏈接庫的HOOK函數(shù)如下

#思路:1.做一個導(dǎo)出函數(shù)StartHook(),由HelloMFC調(diào)用2.安裝一個鼠標(biāo)Hook,將鼠標(biāo)消息再傳輸給主程序
---在HelloMFC的hello.cpp的OnKeyDown消息響應(yīng)函數(shù)中,調(diào)用SetWindowsHookEx()函數(shù),截獲發(fā)送給鍵盤消息響應(yīng)函數(shù)的鍵盤按下消息
---在HOOKDLL.cpp中設(shè)置具體的鉤子的子程序
---設(shè)置鉤子的類型為低水平的鍵盤鉤子:WH_KEYBOAD_LL,可以看到右邊是對應(yīng)的鉤子的處理函數(shù)地址LowLevelKeyboardProc

---WH_KEYBOAD_LL的處理函數(shù)如下:
---如果 nCode 小于零,則掛鉤過程必須返回 CallNextHookEx 返回的值
---如果 nCode 大于或等于零,建議調(diào)用 CallNextHookEx 并返回返回的值
---在CallNextHookEx的功能:可以將鉤子信息傳遞到當(dāng)前鉤子鏈中的下一個子程,一個鉤子程序可以調(diào)用這個函數(shù)之前或之后處理鉤子信息。
---在HOOKDLL.cpp文件中,設(shè)定導(dǎo)出函數(shù)StartHook()
---1.先獲取窗口句柄 2.創(chuàng)建鍵盤HOOK,定義HOOK處理函數(shù) 3.在HOOK處理函數(shù)中,將hook后的信息,傳遞給鉤子鏈的下一個子鉤子
---在HOOKDLL.def將定義的StartHook函數(shù)進行導(dǎo)出
---將HOOKDLL.dll拷貝到HelloMFC的根目錄(這里是動態(tài)鏈接庫)

---在HelloMFC中,對按下F2的鍵盤消息,設(shè)置鍵盤鉤子
---在hello.h中定義函數(shù)StartHook()
---自定義Hook函數(shù),本質(zhì)是調(diào)用DLL中的StartHook函數(shù)
---傳入當(dāng)前窗口的句柄(MFC封裝了,但是源代碼封裝了m_hWnd)
---在鍵盤按下消息響應(yīng)函數(shù),按下F2鍵,就調(diào)用HOOK鍵盤的消息
---HOOK成功就修改窗口名稱
---效果如下

----但是我在消息響應(yīng)函數(shù)里面彈窗,發(fā)現(xiàn)沒有報錯,但是F2沒有進行彈窗
---而且視頻里面也是在HOOKDLL.dll的hook處理函數(shù)進行判斷的(不知道為什么不能再消息響應(yīng)函數(shù)里面進行MessageBox)
---視頻里面是之間在Hook的處理函數(shù)里面LowLevelKeyboardProc函數(shù),存在3個參數(shù)
---不管HOOK成功與否,都將調(diào)用CallNextHookEx將消息傳遞給鉤子鏈的下一個HOOK
---KBDLLHOOKSTRUCT的內(nèi)容如下

---修改HOOK處理函數(shù),對攔截的消息進行預(yù)先處理
---將更新后的HOOKDLL.dll重新復(fù)制到HelloMFC,并且運行
---emmm,我這里還是沒有消息彈窗,難受
#通過自定義消息,如果HOOK成功,向主窗口發(fā)送自定義消息,然后主窗口處理
---在HelloMFC中使用:ON_MESSAGE自定義消息,傳參為自定義的消息類型,消息的響應(yīng)函數(shù)
---然后窗口接到了消息,根據(jù)第一個wParam參數(shù),判斷是否為鍵盤按下消息,如果是的話就彈窗
---我這里存在彈窗不能出現(xiàn)的問題,猜測是W11是不支持吧,建議開個虛擬機在Xp上運行VC6
---效果如下

第十五:MFC對話框
---MFC的對話框就是一個窗口,不僅可以接受消息,還可以被移動和關(guān)閉,還可以在它的客戶區(qū)域進行繪圖
---我們可以把它看成是一個大容器,在上面能夠放置各種各樣的控件,使得程序的支持用戶輸入的手段更加的豐富。

---創(chuàng)建一個MFC的exe文件

---選擇Dialog框(基本對話框)

---去掉所有的添加,只創(chuàng)建一個基本的Dialog框(基本對話框)
---不生成源文件備注,使用靜態(tài)的DLL

---默認(rèn)生成以下文件

---在#include "stdafx.h"之前的默認(rèn)都不編譯,所以111不會報錯
---也就是說:#include "stdafx.h"是MFC的編譯的第一行
---我們可以看到:theApp就是之前全局變量CMyApp
---InitInstance就是之前的WinMain主函數(shù)
---在里面也可以看到消息映射
---點擊DialogTest.rc是一個資源腳本
---可以在rc里面添加一些版本信息等

---可以在項目的目錄下面查看DiaLogTest.rc的部分具體代碼
---可以看到IDD_DIALOGTEST_DIALOG 的風(fēng)格、標(biāo)題、字體、消息等等
---有兩個按鈕,確定、取消 和 “在這里設(shè)置對話控制”
---點擊IDD_DIALOGTEST_DIALOG右邊的窗口圖,然后查看屬性
---發(fā)現(xiàn)對應(yīng)的DiaLogTest.rc的內(nèi)容在這里凸顯

---向Dialog框,添加一個控件

---發(fā)現(xiàn)在DiaLogTest.rc里面也添加了一行按鈕的代碼
---在DiaLogTestDlg.cpp文件中,存在著DoDataExchange()的函數(shù)
---這個是添加相應(yīng)按鈕的響應(yīng)函數(shù),這個函數(shù)是一種動態(tài)綁定技術(shù),
---什么是動態(tài)綁定技術(shù)?
---添加一個名為:IDC_EDIT1的編輯框

---添加按鈕的消息響應(yīng)函數(shù),使得按下按鈕,改變編輯框里面的值

---代碼如下
---發(fā)現(xiàn)編輯框的內(nèi)容發(fā)生了改變

---也可以在:類向?qū)?> 成員變量 > IDC_EDIT1 (控件ID) >添加成員變量
---相當(dāng)于原來是通過GetDlgItem(IDC_EDIT1)來獲取IDC_EDIT1的函數(shù)地址(指針)
---現(xiàn)在通過給Dialog框的控件創(chuàng)建成員變量,進而更好的調(diào)用方法

---通過成員變量,調(diào)用方法來改變編輯框的值
---效果如下

---可以看到,這里的動態(tài)綁定,添加了控件的綁定:IDC_EDIT1和m_edit1進行綁定
---我們操作m_edit1就會改變IDC_EDIT1的控件
---在類向?qū)Ю锩娴某蓡T變量,刪除IDC_EDIT1的成員變量
---添加成員變量和m_edit1綁定,添加Value(之前是Control),Value的類似是CString(字符串?dāng)?shù)組)

---定義m_edit的長度為255

---發(fā)現(xiàn)動態(tài)綁定的地方發(fā)生了改變
---在OnButton1控件的映射函數(shù)修改m_edit1的值,賦值為一個字符串
---發(fā)現(xiàn)編輯框控件的值同樣發(fā)生了改變,之前相當(dāng)于獲取了IDC_EDIT1函數(shù)地址(指針),然后調(diào)用方法修改編輯框的值
---這里是將medit1數(shù)組和IDC_EDIT1關(guān)聯(lián),然后更新編輯框控件的值

---UpdateData(FALSE)和UpdateData(TRUE)的區(qū)別
---例如,在初始化的時候?qū)⒖臻g的內(nèi)容傳遞給edit1
---在顯示的時候,不點擊Button1,IDC_EDIT1編輯框也會顯示test
---在DoModal()里面:就是一個模態(tài)對話框
---在DoModal()里面實現(xiàn)了Dialog框的繪制,輸出、消息循環(huán)
---并且默認(rèn)了兩個按鈕:確認(rèn)和取消,分別對應(yīng)下面的IDOK和IDCANCEL
---在int nResponse = dlg.DoModal();處下斷點,然后點擊確定,發(fā)現(xiàn)nResponse的值為1
---同樣,點擊取消,nResponse的值為2
---可以看到不同的按鈕對應(yīng)不同的值
---為確定按鈕添加消息響應(yīng)函數(shù)

---發(fā)現(xiàn)確定按鈕調(diào)用的是CDialog::OnOK();,在這里下斷點進去查看
---注意,這里是先進入BOOL CDiaLogTestApp::InitInstance()
---然后在??? int nResponse = dlg.DoModal();創(chuàng)建、顯示Dialog框,并設(shè)置消息循環(huán)
---然后再按下確定按鈕,根據(jù)nResponse的值,來調(diào)用CDiaLogTestDlg::OnOK()
---發(fā)現(xiàn)CDiaLogTestDlg::OnOK()的本質(zhì)是調(diào)用EndDialog(IDOK);方法
---如果是CDiaLogTestDlg::IDCANCEL()就調(diào)用EndDialog(IDCANCEL);
---模態(tài)對話框(就是Dialog框)會造成一個結(jié)果:對話框卡在屏幕,雙擊rc

---插入一個新的Dialog框

---新建的Dialog框創(chuàng)建一個名為CDialog1的新類

---然后發(fā)現(xiàn)CDialog1類里面存在2各確定和取消的按鈕
---而且在FileView路徑下

---在DiaLogTest.cpp中,#include "Dialog1.h"
---然后在OnButton1()的響應(yīng)函數(shù)里面
---發(fā)現(xiàn)帶點擊OnButton1,就會彈出第二個Dialg對話框
---而且只能將模態(tài)對話框關(guān)閉之后,才能進行主窗口的操作(可以當(dāng)作驗證,如果不能通過密碼驗證,就不能使用主窗口)

---如果是非模態(tài)對話框,就不能通過DoModal函數(shù)
---但是這樣創(chuàng)建的窗口,只會一閃而過(原因是在void CDiaLogTest2Dlg::OnButton1()
函數(shù)結(jié)束之后,dlg對象就會被釋放掉)
---解決方法:采用在堆中創(chuàng)建對象(new一個對象)
---但是存在問題:new了對象,但是沒有釋放對象
#解決方法1
---在Dialog的取消控件,添加響應(yīng)函數(shù)

---將原來的CDialog::OnCancel();刪除,替換為銷毀窗口
---為什么要改寫呢?是因為:這里原本的方法是對應(yīng)的DoModal的模態(tài)對話框
---這里是非模態(tài)對話框,所以需要重寫取消的函數(shù),刪除對話框
---為Dialog1的PostNCDestroy(在視圖窗口關(guān)閉時最后調(diào)用的成員函數(shù),即刪除視圖對象)消息添加消息響應(yīng)函數(shù)

---由于DestroyWindow();最后會產(chǎn)生PostNcDestroy()消息
---重寫PostNcDestroy()消息,在這里添加銷毀堆內(nèi)存中new的對象
#解決方法2(更簡潔):
---在CDiaLogTest2Dlg.h中,首先#include "Dialog1.h",其次CDialog1* dlg;
---OnButton1() 的消息響應(yīng)函數(shù)不需要聲明CDialog1的指針
---在CDiaLogTest2Dlg的DestroyWindow消息中,建立消息響應(yīng)函數(shù)

---銷毀窗口
---DestroyWindow和PostNcDestroy的區(qū)別
---綜合以上:先調(diào)用DestroyWindow,在此間會有OnDestroy消息,接著窗口被銷毀
---于是DestroyWindow返回TRUE,
---然后是OnNcDestroy消息,之后再調(diào)用PostNcDestroy。
#模態(tài)對話框:
---產(chǎn)生一個窗口卡在前一個窗口,用dlg.DoModual返回參數(shù)
---根據(jù)參數(shù)的返回值來判斷是按下確定還是取消
#非模態(tài)對話框
---不能直接調(diào)用局部變量調(diào)用,需要new一個對象
---然后去創(chuàng)建、顯示,以及手動的釋放對象