一 窗口和消息程序員
1. 前綴:web
2 WPARAM和LPARAM的意義
在Windows是一種16位系統時,WndProc的第三個參數被定義爲WORD,是一個16位的無符號整數,而第四個參數被定義爲一個LONG,是一個32位有符號整數,因此致使對單詞PARAM(參數)加前綴W和L。
但在32位Windows中,WPARAM被定義爲一個UINT,而LPARAM被定義爲一個LONG,所以窗口過程的這兩個參數都是32位的值。算法
3 新的函數類型
WndProc函數返回一個類型爲LRESULT的值,該值是一個LONG型,32位有符號。
WndProc函數被指定爲CALLBACK類型(供系統調用的函數),WinMain函數被指定爲WINAPI類型。這些類型指在Windows自己和用戶的應用程序之間發生的函數調用的特殊調用序列。數組
4 窗口類結構WNDCLASS
有10個字段,分別是:
① style:類風格,用於在何時發出窗口變化消息
② cbClsExtra:在類結構保存的窗口結構中預留一些額外空間
③ cbWndExtra:在Windows內部保存的窗口結構中預留一些額外空間
④ hbrBackground:指定基於這個類建立的窗口背景顏色
⑤ hCursor:讀取光標
⑥ hIcon:讀取圖標
⑦ hInstance:程序的實例句柄
⑧ lpfnWndProc:指定處理基於這個窗口類建立的全部窗口的窗口過程
⑨ lpszClassName:指定類名
⑩ lpszMenuName:指定窗口類菜單數據結構
5 註冊窗口類RegisterClass
通常在Windows XP及之後均可以很順利的註冊成功。因此能夠只寫RegisterClass(&wndclass);框架
6 建立窗口CreateWindow
窗口類定義了窗口的通常特徵,調用CreateWindow能夠指定有關窗口的更詳細的信息。異步
hwnd = CreateWindow (szAppName, // 指定一個窗口類,基於該窗口類建立窗口 TEXT ("Hello Win"), // 這個字符串會出如今標題欄中 WS_OVERLAPPEDWINDOW, // 本窗口風格 CW_USEDEFAULT, //窗口左上角的X座標 CW_USEDEFAULT, //窗口左上角的Y座標 CW_USEDEFAULT, //窗口的寬度 CW_USEDEFAULT, //窗口的高度 NULL, //窗口對象的父窗口句柄 NULL, //窗口對象的菜單句柄或者子窗口編號 hInstance, //當前進程的實例句柄 NULL) ; //窗口對象的參數指針句柄
建立窗口返回的是窗口句柄。ide
7 顯示窗口
窗口建立成功後,系統將在內存中爲其分配一塊內存,可是此時窗口並未顯示在顯示器上,因此須要使用兩個調用。
① ShowWindow(窗口句柄,iCmdShow);
其中的第二個參數用於肯定如何兒子屏幕上顯示窗口,是最小化仍是常規仍是最大化。
② UpdateWindow(窗口句柄)
調用上句將致使客戶區被繪製。它經過給窗口過程發送一個WM_PAINT消息來作到這一點。函數
8 消息循環
調用UpdateWindow以後,窗口就出如今顯示器上。
Windows爲當前運行的每一個Windows程序維護一個「消息隊列」。在發生事件的時候,Windows將事件轉換爲一個「消息」,並將消息放入程序的消息隊列中。
程序經過執行一個叫作「消息循環」的代碼從消息隊列中取出消息。(注意GetMessage(阻塞,若沒有消息則不返還控制權)和PeekMessage(非阻塞,若沒有消息也會返回)的區別)工具
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } //其中msg是一個類型爲MSG的結構,MSG結構在WINUSER.H中定義以下: typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG /* 其中: hwnd 是消息發向的窗口句柄。 message 是消息標識符,以WM_開頭。 wParam 一個32位的消息參數。 lParam 一個32位的消息參數。 time 消息放入消息隊列的時間。 pt 消息放入消息隊列時鼠標的座標。*/
消息循環以GetMessage調用開始,它從消息隊列中取出一個消息,GetMessage(&msg, NULL, 0, 0)這一調用傳給Windows一個指向名爲msg的MSG結構的指針。其他三個參數設置爲NULL或0,表示程序接收本身建立的全部窗口的全部消息。 只要從消息隊列中取出的消息的message域不爲WM_QUIT,GetMessage就返回一個非0值,while循環就能夠繼續。
TranslateMessage(&msg); 將msg結構傳給Windows,進行一些鍵盤轉換。 DispatchMessage(&msg); 將msg結構傳給Windows,而後Windows將裏面的消息發給相應的窗口過程進行處理。處理後,WndProc返回到Windows,Windows返回到程序,程序繼續下一個while循環。
9 窗口過程WndProc
實際的動做發生在窗口過程當中。窗口過程肯定了在窗口的客戶區顯示什麼,以及怎麼處理用戶輸入。
(1)窗口過程是命名爲WndProc的函數。(也能夠其餘不衝突的名字)
(2)一個Windows程序能夠包含多個窗口過程。
(3)一個窗口過程老是與調用RegisterClass註冊的特定窗口類相關聯。
(4)CreateWindow函數根據特定的窗口類建立一個窗口,返回該窗口的句柄。
(5)可是基於一個窗口類能夠建立多個窗口。
1程序 —— 包括 ——n個窗口過程
1窗口過程 —— 關聯 —— 1窗口類
1窗口類 —— 建立 —— n個窗口
CreateWindows根據窗口類建立一個窗口。
窗口過程老是聲明成以下形式:
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); /*這4個參數跟MSG結構的前4個域是相同的。 第一個參數:接收消息的窗口句柄。 第二個參數:消息類型,標識消息的數字 最後兩個參數:32位的消息參數。 */
程序一般不直接調用窗口過程,而是由Windows調用窗口過程。
10 窗口過程處理消息
窗口過程接收的每一個消息均是用一個數值來標識的,也就是傳給窗口過程的message參數。
窗口過程處理消息時,必須返回0.窗口過程不予處理的全部消息應該被傳給名爲DefWindowsProc的Windows函數。
用switch語句來處理不一樣消息,消息以WM_開頭。
11 WM_CREATE消息
當Windows在處理CreateWindow函數時,窗口過程就會接收到WM_CREATE消息。
一般,窗口過程在WM_CREATE處理期間進行一次窗口初始化。
12 WM_PAINT消息
當窗口客戶區域的一部分或所有變成「無效」時,必須進行刷新,WM_PAINT將通知程序。
在最初建立窗口的時候,整個客戶區都是無效的,由於程序尚未在窗口上畫任何東西。在調用UpdateWindow時,一般會觸發第一個WM_PAINT消息,指示窗口過程在客戶區域上畫一些東西。
在改變程序窗口大小後,客戶區也會變得無效,至於怎麼變得無效由CS_引導的類風格選項肯定。
對WM_PAINT的處理幾乎老是從一個BeginPaint調用開始,以一個EndPaint結束。
hdc = BeginPaint(hwnd, &pt); //do something;(如GetClientRect(hwnd, &rect);) EndPaint(hwnd, &pt);
hwnd是要刷新的窗口的窗口句柄。
pt是指向類型爲PAINTSTRUCT的結構指針。
在BeginPaint調用中,若是客戶區域的背景還未被擦除,就由Windows來擦除。而後使用註冊窗口類的WNDCLASS結構中的hbrBackground域中第一的刷子來刪除背景。
BeginPaint調用使整個客戶區有效,並返回一個「設備環境句柄」。設備環境是指物理輸出設備及其驅動程序。能夠利用該「設備環境句柄」在客戶區域顯示文本和圖形。
EndPaint調用釋放設備環境句柄。
GetClientRect(hwnd, &rect);
第一個參數:程序的窗口句柄;
第二個參數:指向RECT類型的rectangle結構。該結構有4個LONG域,標識客戶區域的尺寸。
當改變窗口大小時,WndProc經過調用GetClientRect來獲取變化後的窗口大小,從新繪製客戶區。
13 WM_DESTROY消息
當用戶點擊關閉按鈕時發生。程序能夠經過調用PostQuitMessage以標準方式響應WM_DESTROY消息;
PostQuitMessage(0); // 該函數在程序的消息隊列插入一個WM_QUIT消息。
GetMessage對於除了WM_QUIT消息以外的從消息隊列中取出的全部消息都返回非0值。而當GetMessage取到一個WM_QUIT消息時,返回0.
14 關閉程序時的消息傳遞
① 用戶點擊關閉按鈕
② 產生WM_SYSCOMMAND消息;
③ 產生WM_CLOSE消息響應WM_SYSCOMMAND;
④ 產生WM_DESTROY消息響應WM_CLOSE;
⑤ 產生WM_QUIT消息響應WM_DESTROY。
15 進隊消息和不進隊消息
消息可以分爲:進隊消息和不進隊消息。
進隊消息是由Windows放入程序消息隊列中的。在程序的消息循環中,從新返回並分配給窗口過程。
不進隊消息在Windows調用窗口時直接發送給窗口過程。
也就是說,進隊消息發送給消息隊列,不進隊消息發送給窗口過程。
在任何狀況下,窗口過程都將得到窗口全部的消息。窗口過程是窗口的消息中心。
進隊消息基本上是用戶輸入的結果,還包括時鐘消息、刷新消息、退出消息。
不進隊消息基本上是來自調用特定的Windows函數。
--------------------------------------------------------------------------------------------------
二 輸出文本
16 有效矩形和無效矩形
窗口過程一旦接受到WM_PAINT消息以後,就準備更新整個客戶區,但每每只需更新一個較小的區域。這個區域就稱爲「無效區域」。正是客戶區內存在無效區域,才提示Windows將一個WM_PAINT消息放入消息隊列。
Windows內部爲每一個窗口保存一個「繪圖信息結構」,這個結構包含了包圍無效區域的最小矩形的座標以及其餘信息,這個矩形就叫作「無效矩形」。
若是在窗口過程處理WM_PAINT消息以前,客戶區又有一個區域變爲無效,那麼Windows計算出一個包圍兩個無效區域的新的無效矩形,並將這個變化後的信息放在繪製信息結構中。
一個消息隊列在一個時刻只能有一個WM_PAINT消息在隊列中。
窗口過程能夠調用InvalidateRect使客戶區變爲無效。若是消息隊列包含一個WM_PAINT消息,那麼Windows將計算出新的無效矩形;不然,就在消息隊列中添加一個WM_PAINT消息。
在處理WM_PAINT消息期間,窗口過程在調用了BeginPaint以後,整個客戶區就會變得有效。
程序也能夠顯式調用ValidateRect函數使客戶區內的任意矩形區域變得有效。若是這條調用使整個客戶區都有效,那麼將在當前消息隊列中刪除WM_PAINT消息。
17 設備環境
要在窗口的客戶區繪圖,可使用Windows的圖形設備接口GDI函數。
設備環境DC是GDI內部保存的數據結構。
設備環境與特定的顯示設備有關。
設備環境中的有些值是圖形化的「屬性」,如指出顏色、背景色、座標映射方式等。
當程序要繪圖時,必須先獲取設備環境句柄。在獲取了該句柄以後,Windows用默認的屬性值填充設備環境結構的內部各域。
當程序在客戶區繪圖完畢後,必須釋放設備環境句柄。句柄被釋放後再也不有效,也再也不使用。程序必須在處理單個消息期間獲取和釋放句柄。
18 獲取設備環境句柄的方法之一
在使用WM_PAINT消息時,使用這種方法。它涉及到BeginPaint和EndPaint兩個函數。
在處理WM_PAINT消息時,窗口過程首先調用BeginPaint。BeginPaint函數通常在準備繪製時致使無效區域的背景被擦除。BeginPaint返回的值是設備環境句柄,這一返回值一般被保持在叫作hdc的變量中。
HDC hdc;
HDC數據類型定義爲32位的無符號數。
而後,程序就可使用須要設備環境句柄的GDI函數了。
調用EndPaint便可釋放設備環境句柄。
通常地,處理WM_PAINT消息的形式以下:
case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //GDI函數 EndPaint(hwnd, &ps); return 0;
處理WM_PAINT消息時,必須成對地調用BeginPaint和EndPaint。
19 繪圖信息結構
Windows爲每個窗口保存一個繪圖信息結構。這就是PAINTSTRUCT,定義以下:
typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT
在用BeginPaint時,Windows填充該結構的各個字段。用戶程序只須要使用前三個字段。
hdc是設備環境句柄。
fErase一般被標識爲FLASE,這意味着Windows已經擦除了無效矩形的背景。
若是程序經過調用Windows函數InvalidateRect使客戶區中的矩形失效,那麼該函數的最後一個參數會指定fErase的值。若是指定0,那麼在稍後的PAINTSTRUCT裏面的fErase會被設置爲TRUE。
rcPaint是RECT結構,定義了無效矩形的邊界。RECT結構中的left、top、right、bottom以像素點爲單位。此時,Windows將繪圖操做限制在此RECT結構定義的矩形範圍內,若是要在無效矩形外繪圖,應該在調用BeginPaint以前,使用以下調用:
InvalidateRect(hwnd, NULL, TRUE);
它將使整個客戶區無效,並擦除背景。
20 取設備環境句柄的方法之二
要獲得窗口客戶區的設備環境句柄,能夠調用GetDC來獲取句柄。在使用完後調用ReleaseDC;
hdc = GetDC(hwnd); //使用GDI函數 ReleaseDC(hwnd, hdc);
GetDC和ReleaseDC函數必須成對地使用。
GetDC返回的設備環境句柄具備一個剪取矩形,等於整個客戶區。
GetDC不會使任何無效區域變爲有效,要是整個客戶區有效,須要調用ValidateRect(hwnd, NULL);
通常能夠調用GetDC和ReleaseDC來對鍵盤消息、鼠標消息做出反應。
21 TextOut細節
TextOut是用於顯示文本的最經常使用的GDI函數。語法是:
TextOut(hdc, x, y, psText, iLength);
第一個參數:設備環境句柄,既能夠是GetDC的返回值,也能夠是BeginPaint的返回值。
第二個參數:定義客戶區內字符串的開始位置的水平座標。
第三個參數:定義客戶區內字符串的開始位置的垂直座標。
第四個參數:指向要輸出的字符串的指針。
第五個參數:字符串中字符的個數。若是psText中的字符是Unicode的,那麼串中的字節數就是iLength值的兩倍。
設備環境還定義了一個剪取區域。
對於從GetDC獲取的設備環境句柄,默認的剪取區是整個客戶區。
對於從BeginPaint獲取的設備環境句柄,默認的剪取區是無效區域。
Windows不會在剪取區域以外的任何位置顯示字符串。
22 字符大小
要用TextOut顯示多行文本,就必須肯定字體的字符大小,能夠根據字符的高度來定位字符的後續行,以及根據字符的寬度來定位字符的後續列。
系統字體的字符高度和平均寬度取決於視頻顯示器的像素大小。
程序能夠調用GetSystemMetrics函數來肯定關於用戶界面構件大小的信息。
程序能夠調用GetTextMetrics函數來肯定字體大小。
metric是度量的意思。
TEXTMETRIC的結構:
typedef struct tagTEXTMETRIC { LONG tmHeight; LONG tmAscent; LONG tmDescent; LONG tmInternalLeading; LONG tmExternalLeading; LONG tmAveCharWidth; LONG tmMaxCharWidth; 其餘字段 } TEXTMETRIC, *PTEXTMETRIC;
要使用GetTextMetrics函數,須要先定義一個一般被稱爲tm的結構變量:
TEXTMETRIC tm;
在須要肯定文本尺寸時,先要獲取設備環境句柄,再調用GetTextMetrics:
hdc = GetDC(hwnd); GetTextMetrics(hdc, &tm); // 操做; ReleaseDC(hwnd,hdc);
23 文本尺寸
字體的縱向大小由5個值肯定:
① tmHeight,等於tmAscent加上tmDescent。這兩個值表示了基線上下字符的最大縱向高度。
② tmAscent,基線以上的高度
③ tmDescent,基線如下的高度
④ tmInternalLeading,重音號和字符之間的距離,如ü中的u和兩點的距離。
⑤ tmExternalLeading,通常用於多行文本間行距的調整。
字符的橫向大小由2個值肯定:
① tmAveCharWidth,小寫字母加權平均寬度。
② tmMaxCharWidth,字體中最寬字符的寬度。
對於等寬字體,tmAveCharWidth和tmMaxCharWidth這兩個值相等。
大寫字母的平均寬度比較複雜,若是:
① 字體是等寬字體,那麼大寫字母的平均寬度等於tmAveCharWidth。
② 字體是變寬字體,那麼大寫字母的平均寬度等於tmAveCharWidth*1.5。
判斷字體是不是變寬字體,能夠經過TEXTMETRIC結構中的tmPitchAndFamily域的低位判斷,若是低位是1,那麼是變寬字體,若是是0,那麼是等寬字體。
大寫字母寬度 = (tm.tmPitchAndFamily & 1 ? 3 : 2) / 2 * 小寫字母寬度
24 格式化文本
在一次Windows對話期間,系統字體的大小不會改變,所以在程序運行過程當中,只須要調用一次GetTextMetric。最好是在窗口過程當中處理WM_CREATE消息時進行此調用。
假設要編寫一個Windows程序,在客戶區顯示多行文本,這須要先獲取字符寬度和高度。能夠在窗口過程內定義兩個變量來保存字符寬度和總的字符高度。
case WM_CREATE: hdc = BeginPaint(hwnd, &pt); GetTextMetric(hdc, &tm); cxChar = tm.tmAveCharWidth; 小寫字母寬度 cyChar = tm.Height + tm.tmExternalLeading; 字母高度 cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) / 2 * tm.tmAveCharWidth; 大寫字母寬度 EndPaint(hwnd, &pt); return 0;
25 客戶區的大小
窗口最大化以後的客戶區大小,能夠經過以SM_CXFULLSCREEN和SM_CYFULLSCREEN爲參數調用GetSystemMetric來得到。
要肯定客戶區的大小,最好的方法是在窗口過程處理WM_SIZE消息。在窗口大小改變時,就會產生WM_SIZE消息。傳給窗口過程的lParam參數的低位字中包含客戶區的寬度x,高位字中包含客戶區的高度y。要保存這些尺寸,能夠定義兩個int型變量來保存。
static int cxClient,cyClient;
而後在WM_SIZE消息處理中:
case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); return 0;
用cyClient/cyChar能夠獲得客戶區能夠顯示的文本總行數。
26 滾動條的範圍和位置
每一個滾動條都有一個相關的範圍和位置。這是一對整數。當滾動框在滾動條的頂部(左部)時,滾動框的位置是範圍的最小值;在滾動條的底部(右部)時,滾動框的位置是範圍的最大值。
在默認狀況下,滾動條的範圍是0~100,但將範圍改變爲更方便於程序的數值也是很容易的:
SetScrollRange(hwnd, iBar, iMin, iMax, bRedraw); /* hwnd爲該窗口的句柄。 iBar爲SB_VERT或SB_HORZ。 iMin和iMax爲範圍。 bRedraw,若是要Windows根據新範圍重繪滾動條,則設置爲TRUE。 */
滾動框的位置不是連續的,而是離散的整數值。
可使用SetScrollPos在滾動條範圍內設置新的滾動框位置:
SetScrollPos(hwnd, iBar, iPos, bRedraw);
參數iPos是新位置,必須在iMin至iMax的範圍內。
Windows提供了相似的函數GetScrollRange和GetScrollPos來獲取滾動條的當前範圍和位置。
27 滾動條消息
在用鼠標單擊滾動條或者拖動滾動框時,Windows都給窗口過程發生WM_VSCROLL或WM_HSCROLL消息。在滾動條上的每一個鼠標動做都至少產生兩個消息,一個在按下鼠標鍵時產生,一個在釋放鼠標鍵時產生。
WM_VSCROLL和WM_HSCROLL也帶有wParam和lParam消息參數。
lParam只用於做爲子窗口而建立的滾動條(一般在對話框內)。
wParam消息參數被分爲一個低位字和一個高位字。
低位字是一個數值,指出了鼠標對滾動條進行的操做。這個數值被看做一個「通知碼」。通知碼以SB開頭。
#define SB_LINEUP 0 #define SB_LINELEFT 0 #define SB_LINEDOWN 1 #define SB_LINERIGHT 1 #define SB_PAGEUP 2 #define SB_PAGELEFT 2 #define SB_PAGEDOWN 3 #define SB_PAGERIGHT 3 #define SB_THUMBPOSITION 4 #define SB_THUMBTRACK 5 #define SB_TOP 6 #define SB_LEFT 6 #define SB_BOTTOM 7 #define SB_RIGHT 7 #define SB_ENDSCROLL 8
當把鼠標的光標放在滾動框上並按住鼠標鍵時,就產生SB_THUMBPOSITION和SB_THUMBTRACK消息。
當wParam的低位字是SB_THUMBTRACK時,wParam的高位字是用戶在拖動滾動框時的當前位置。
當wParam的低位字是SB_THUMBPOSITION時,wParam的高位字是用戶釋放鼠標後滾動框的最終位置。
28 滾動條信息函數
滾動條文檔指出SetScrollPos、SetScrollRange、GetScrollPos、GetScrollRange函數是過期的。
在Win32 API中,升級了2個滾動條函數,稱做SetScrollInfo和GetScrollInfo。這些函數完成上述4個函數的所有功能,並增長了2個新特性。
第一個功能設計滾動框的大小。滾動框的大小稱做頁面大小。算法是:
滾動框大小 / 滾動長度 ≈ 頁面大小 / 範圍 ≈ 顯示的文檔數量 / 文檔的總大小
可使用SetScrollInfo來設置頁面大小。
第二個功能是GetScrollInfo函數,它能夠獲取32位的範圍值。
SetScrollInfo和GetScrollInfo函數的語法是:
SetScrollInfo(hwnd, iBar, &si, bRedraw); GetScrollInfo(hwnd, iBar, &si); //ar參數是SB_VERT或SB_HORZ。 //Redraw能夠是TRUE或FALSE,指出了是否要Windows從新繪製計算了新信息後的滾動條。 //函數的第三個參數是SCROLLINFO結構,定義爲: typedef struct tagSCROLLINFO { UINT cbSize; UINT fMask; int nMin; int nMax; UINT nPage; int nPos; int nTrackPos; } SCROLLINFO
在程序中,能夠定義以下的SCROLLINFO結構類型:
SCROLLINFO si;
在調用SetScrollInfo或GetScrollInfo函數以前,必須將cbSize自動設置爲結構的大小:
si.cbSize = sizeof(si);或si.cbSize = sizeof(SCROLLINFO);
fMask:把fMask字段設置爲以SIF前綴開頭的一個或多個標識,來獲取或設置裏面的結構中域的值。
① SIF_RANGE:用於獲取或設置滾動條的範圍
② SIF_POS:用於獲取或設置滾動框的位置
③ SIF_PAGE:用於獲取或設置滾動條的頁面大小
④ SIF_TRACKPOS:用於獲取或設置滾動框移動時的位置
-------------------------------------------------------------------------------------------------------------------
三 圖形基礎
29 GDI基礎(微軟GDI文檔GDI Reference)
圖形設備接口GDI是Windows的子系統,它負責在視頻顯示器和打印機上顯示圖形。
Windows NT中的圖形主要由GDI32.DLL動態連接庫輸出的函數來處理。
GDI的主要目的之一是支持與設備無關的圖形。
圖形輸出設備分爲兩大類:光柵設備和矢量設備。
大多數PC機顯示器、打印機都是光柵設備。
繪圖儀是矢量設備。
組成GDI的函數能夠分爲這樣幾類:
① 獲取(或重建)和釋放(或清除)設備環境的函數;
② 獲取有關設備環境信息的函數;
③ 繪圖函數;
④ 設置和獲取設備環境參數的函數;
⑤ 使用GDI對象的函數。
30 GDI圖元
在屏幕或打印機上顯示的圖形類型自己能夠被分爲幾類,一般被稱爲「圖元」。
① 直線和曲線
② 填充區域
③ 位圖:位圖是位的矩形數組,位對應於顯示設備上的像素,它們是光柵圖形的基本工具。GDI支持兩種類型的位圖——老的「設備有關」位圖,新的「設備無關」位圖。
④ 文本
31 GDI其餘方面
① 映射模式和變化;
② 元文件:元文件是以二進制形式存儲的GDI命令的集合。元文件主要用於經過剪貼板傳輸矢量圖形表示。
③ 區域:區域是形狀任意的複雜區;
④ 路徑:路徑是GDI內部存儲的直線和曲線的集合;
⑤ 剪裁:繪圖能夠限制在客戶區的某一部分中,這就是剪裁。剪裁一般是經過區域或者路徑定義的。
⑥ 調色板:定製調色板一般限於顯示256色的顯示器。Windows僅保留這些色彩之中的20種供系統使用,但能夠改變其餘236種色彩。
⑦ 打印
32 進一步探討設備環境
想在一個圖形輸出設備上繪圖時,首先必須得到一個設備環境的句柄。將句柄返回給程序時,Windows就給了用戶使用設備的權限。而後在GDI函數中將該句柄做爲一個參數,向Windows標識想在其上進行繪圖的設備。
(1)獲取設備環境句柄
若是在處理一條消息時獲取了設備環境句柄,應該在退出窗口函數以前釋放它。
獲取設備環境句柄的幾種方法:
① 在處理WM_PAINT消息時,使用BeginPaint和EndPaint調用
hdc = BeginPaint(hwnd, &ps); //GDI操做 EndPaint(hwnd, &ps);
注:變量ps是類型爲PAINTSTRUCT的結構,該結構的hdc字段是BeginPaint返回的設備環境句柄。PAINTSTRUCT結構包含了一個名爲rcRect的RECT結構,該結構定義了包圍窗口無效範圍的矩形。使用從BeginPaint得到的設備環境句柄,只能在這個區域內繪圖。BeginPaint調用使這個區域有效。
② 能夠在處理非WM_PAINT消息時獲取設備環境句柄
hdc = GetDC(hwnd); //GDI操做 ReleaseDC(hwnd, hdc);
注:這個設備環境適用於窗口句柄爲hwnd的客戶區。這個調用能夠在整個客戶區上繪圖。
③ Windows程序還能夠獲取適用於整個窗口的設備環境句柄
hdc = GeWindowstDC(hwnd); //GDI操做 ReleaseDC(hwnd, hdc);
注:這個設備環境除了客戶區以外,還包括窗口的標題欄、菜單、滾動條和框架。若是要使用該函數,必須捕獲WM_NCPAINT消息。
④ 獲取整個屏幕的設備環境句柄
hdc = CreateDC(TEXT(「DISPLAY」), NULL, NULL, NULL);
原型是:
hdc = CreateDC(pszDriver, pszDevice, pszOutput, pData); //GDI操做 DeleteDC(hdc);
⑤ 若是隻須要獲取關於某設備環境的一些信息,而並不進行任何繪畫,在這種狀況下,可使用CreateIC來獲取一個「信息描述表」的句柄,其參數和CreateDC同樣。
⑥ 一個設備環境一般是指一個物理顯示設備。一般,須要獲取有關該設備的信息,其中包括顯示器的顯示尺寸和色彩範圍。能夠經過GetDeviceCaps函數來獲取這些信息。
iValue = GetDeviceCaps(hdc, iIndex);
參數iIndex的取值爲WINGDI.H頭文件中定義的29個標識符之一。
33 用TextOut輸出整型的方法
設一開始有整型:int i = 100;
要用TextOut函數將i輸出,須要用到三個函數:
① wsprintf
② TEXT宏
③ TextOut
首先得先說明下wsprintf的原型:
int wsprintf(LPTSTR lpOut, LPCTSTR lpFmt,...); /* 一個參數:緩衝區,是一個字符數組,通常定義爲TCHAR型。 第二個參數:格式字符串,由於第一個參數是TCHAR類型,必定要和TEXT宏聯合使用,這樣才能在不一樣的編譯環境下均可以順利編譯。 後續參數:要輸出的的整型變量。 * //第一個參數:要定義一個TCHAR的字符數組做爲緩衝區。 TCHAR szBuffer[10]; //足夠大就好了 //第二個參數,須要使用到TEXT宏。 //XT宏的原型: TEXT(LPTSTR string //ANSI or Unicode string); //處理要轉換的整型,具體用法是: TEXT(「%d」); //上述的兩個調用應該寫成: int iLength = 0; //用來保存字符串中的字符個數; iLength = wsprintf(szBuffer, TEXT(「%d」), i); //語句的做用是:將i存進szBuffer中,返回szBuffer存有的字符個數到iLength中。 TextOut的原型是: TextOut(hdc, x, y, psText, iLength); /* 數是設備環境句柄; 第二個參數是輸出的文本的x座標; 第三個參數是輸出的文本的y座標; 第四個參數是是指向要輸出的字符串的指針; 第五個參數是字符串中的字符個數; */ //那麼TextOut函數應該寫成: TextOut(hdc, x, y, szBuffer, iLength);
34 設備的大小
使用GetDeviceCaps函數能獲取有關輸出設備物理大小的信息。
對於打印機,用「每英寸的點數dpi」表示分辨率。
對於顯示器,用水平和垂直的總的像素數來表示分辨率。
用「像素大小」或「像素尺寸」表示設備水平或垂直顯示的總像素數。
用「度量大小」或「度量尺寸」表示以每英寸或毫米爲單位的顯示區域的大小。
像素大小 / 度量大小 = 分辨率
使用SM_CXSCREEN和SM_CYSCREEN參數從GetDeviceCaps獲得像素大小;
使用HORZSIZE和VERTSIZE參數從GetDeviceCaps獲得度量大小;
二者相除就能夠獲得水平分辨率和垂直分辨率。
若是設備的水平分辨率和垂直分辨率相等,就稱該設備具備「正方形像素」。
由於整個屏幕的度量大小是固定的,因此能夠根據分辨率調整水平或垂直顯示的像素數。若是分辨率小,那麼「像素大小」也就小,也就是說,總像素數少了,那麼每一個像素的尺寸也就變得大些。
35 字體的大小
如今討論字體的大小問題,這裏不是說字號,而是說字體顯示的dpi值。Windows系統默認是每英寸96點,所另一種選擇,就是每英寸120點。
咱們在調整分辨率的時候,從小分辨率變化到大分辨率時,會以爲圖標的文字變小,那是由於在大分辨率下,每一個像素的面積變小,假設一個字須要100個像素來顯示,那麼從小分辨率變化到大分辨率時,字的總面積就變小了,因此字的大小也就發生變化,而這一變化是字體的大小變化,而不是該字的字號發生變化。
在傳統的排版中,字體的字母大小由「磅」表示。1磅≈1/72英寸,在計算機排版中1磅正好爲1/72英寸。
理論上,字體的磅值是從字體中最高的字符頂部到字符下部的字符底部的距離,其中不包括重音號。根據TEXTMETRIC結構,字體的磅值等於tmHeight – tmInternalLeading。
36 關於色彩 「全色」視頻顯示器的分辨率是每一個像素24位:8位紅色、8位綠色、8位藍色。 「高彩色」顯示分辨率是每一個像素16爲:5位紅色、6位綠色、5位藍色。 顯示256種顏色的視頻適配器每一個像素須要8位。然而這些8位的值通常由定義實際顏色的調色板表組織。 使用GetDeviceCaps可使程序員肯定視頻適配器的存儲組織,以及可以表示的色彩數目。 這個調用返回色彩平面的數目:iPlanes = GetDeviceCaps(hdc, PLANES); 這個調用返回每一個像素的色彩位數:iBitsPixel = GetDeviceCaps(hdc, BITSPIXEL); 大多數彩色圖形顯示設備要麼使用多個色彩平面,要麼每像素有多個色彩位,可是不能同時兩者兼用;即這兩個調用必須有一個返回1.(通常都是第一個返回1)。 在大多數GDI函數調用中,使用COLORREF值(32位)來表示一種色彩。
理論上,COLORREF能夠指定2的24次方或1600萬種色彩。 這個無符號長整數經常稱爲一個「RGB色彩」。在使用RGB(r, g, b);宏時注意參數的順序是紅、綠、藍。而在無符號長整數中,由高位到低位是0、藍、綠、紅。 當三個參數都是0時,表示黑色,當三個參數都是255時,表示白色。 黑色 = RGB(0,0,0) = 0x00000000 白色 = RGB(255, 255, 255) = 0x00FFFFFF
37 保存設備環境
一般,在調用GetDC或BeginPaint時,Windows會用默認值建立一個新的設備環境,對設備環境其屬性所作的一切修改在調用ReleaseDC或EndPaint被釋放掉。
若是須要使用非默認的設備環境屬性,則必須在每次獲取設備環境句柄時初始化設備環境。
若是須要在釋放設備環境以後,仍然保存程序中對設備環境所作的改變,以便在下一次調用GetDC和BeginPaint時它們仍起做用。則應該在窗口類那將CS_OWNDC標誌包含進窗口類風格中。
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
如今,基於這個窗口類所建立的每一個窗口都將擁有本身的設備環境,它一直存在,直到窗口被刪除。
若是使用了CS_OWNDC風格,就只需初始化設備環境一次,能夠在處理WM_CREATE消息期間完成這一操做。
CS_OWNDC風格隻影響GetDC和BeginPaint得到的設備環境,不影響其餘函數得到的設備環境,如GetWindowDC得到的設備環境。
在某些狀況下,能夠須要改變某些設備環境,用改變後的屬性進行繪圖,而後又要恢復回改變前的屬性。這時,能夠經過以下調用來保存設備環境的狀態。
//保存: int idSaved = 0; idSaved = SaveDC(hdc); //恢復: RestoreDC(hdc, idSaved);
也能夠不保存SaveDC的返回值,這時候若是要恢復,就只能恢復到最近保存的狀態,RestoreDC(hdc, -1);
38 寫像素
寫像素SetPixel(hdc, x, y, crColor);其中:
hdc是設備環境句柄;
x, y是像素點的座標;
crColor是要設置的顏色,通常能夠用RGB(r, g, b)設置。
39 線條
幾種畫線函數:
① LineTo:畫直線
② Polyline和PolylineTo:畫一系列相連的直線
③ PolyPolyline:畫多組相連的線
④ Arc和ArcTo和AngleArc:畫橢圓線
⑤ PolyBezier和PolyBezierTo:畫貝塞爾線條
⑥ PolyDraw:畫一系列相連的線以及貝塞爾線條
幾種填充函數:
① Rectangle:畫矩形
② Ellipse:畫橢圓
③ RoundRect:畫帶圓角的矩形
④ Pie:畫橢圓的一部分,使其看起來像一個扇形
⑤ Chord:畫橢圓的一部分,使其看起來像弓形
⑥ Polygon:畫多邊形
⑦ PolyPolygon:畫多個多邊形
設備環境的5個屬性影響着用這些函數所畫線條的外觀:
① 當前畫筆的位置;
② 畫筆;
③ 背景方式;
④ 背景色;
⑤ 繪圖模式。
畫一條直線,必須調用2個函數,第一個函數指定了線的開始點座標,第二個函數指出了線的終點座標:
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
MoveToEx不會畫線,只是設置了設備環境的「當前位置」屬性。而後LineTo函數從當前的位置到它所指定的點畫一直線。在默認的設備環境中,當前位置最初是在點(0,0)。
MoveToEx最後一個參數是指向POINT結構的指針。從該函數返回後,POINT結構的x和y字段指出了以前的「當前位置」,若是不須要這個信息,直接填NULL。
若是須要獲取當前位置,先定義一個POINT的結構變量pt,而後經過下面的調用:
GetCurrentPositionEx(hdc, &pt);
幾個函數的原型:
Rectangle(hdc, xLeft, yTop, xRight, yBottom);
Ellipse(hdc, xLeft, yTop, xRight, yBottom);
RoundRect(hdc, xLeft, yTop, xRight, yBottom, xCorner, yCorner);
Chord(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Pie(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
Arc(hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd, yEnd);
一個二維的貝塞爾線條由4個點定義——兩個端點和兩個控制點。曲線的控制點固定,將曲線從兩個端點間的直線處拉伸構造麴線。
40 使用畫筆
調用任何畫筆函數時,Windows使用設備環境中當前選中的「畫筆」來畫線。畫筆決定線的色彩、寬度、線型。線型能夠是實線、點劃線、虛線,默認設備環境中畫筆是BLACK_PEN,一個像素寬,實線。
Windows提供三種現有畫筆,分別是:BLACK_PEN, WHITE_PEN和NULL_PEN。
Windows使用句柄來引用畫筆。用HPEN的類型定義,即畫筆的句柄。
HPEN hPen;
調用GetStockObject,能夠得到現有畫筆的句柄。
hPen = GetStockObject(WHITE_PEN);
調用SelectObject將畫筆選進設備環境。
SelectObject(hdc,hPen);
SelectObject的返回值是選進前設備環境的畫筆句柄。
使用CreatePen或CreatePenIndirect建立一個「邏輯畫筆」,這僅僅是對畫筆的描述。這些函數返回邏輯畫筆的句柄,而後調用SelectObject將畫筆選進設備環境,以後纔可使用新的畫筆來畫線。
在任什麼時候候,只能有一種畫筆選進設備環境。
在釋放設備環境或在選擇了另外一種畫筆到設備環境中以後,就能夠調用DeleteObject來刪除所建立的邏輯畫筆。
邏輯畫筆是一種「GDI對象」,GDI對象有六種:畫筆、刷子、位圖、區域、字體、調色板。
CreatePen的原型是:
HPEN CreatePen(iPenStyle, iWidth, crColor);
iPenStyle參數肯定畫筆是實線、虛線仍是點線。
iWidth參數肯定線寬,若是iPenStyle不是實線,且iWith大於1,那麼畫筆將變成實線。
crColor是RGB顏色。
獲取當前畫筆句柄:
hPen = GetCurrentObject(hdc, OBJ_PEN);
還能夠創建一個邏輯畫筆LOGPEN結構,調用CreatePenIndirect來建立畫筆。
LOGPEN logpen;
此結構有三個成員:UINT lopnStyle 是畫筆線型;POINT lopnWidth是按邏輯單位度量的畫筆寬度,只用其中的x值;COLORREF lopnColor是畫筆顏色
41 填充空隙
點式畫筆和虛線畫筆的空隙的着色取決於設備環境的兩個屬性——背景模式和背景顏色。默認的背景模式是OPAQUE,在這種方式下,Windows使用背景色填充空隙,默認的背景色爲白色。
下述調用用來改變和獲取Windows用來填充空隙的背景色:
改變:SetBkColor(hdc, crColor);
獲取:GetBkColor(hdc);
下述調用用來改變和獲取背景模式:
改變:SetBkMode(hdc, 模式);
模式:TRANSPARENT,忽略背景色,而且不填充空隙。
OPAQUE默認。
獲取:GetBkMode(hdc);
42 繪圖方式
設備環境中定義的繪圖方式也影響顯示器上所畫線的外觀。
當Windows使用畫筆來畫線時,實際上執行畫筆像素與目標位置處原來像素之間的某種按位布爾運算。像素間的按位布爾運算叫作「光柵運算」,簡稱爲「ROP」。因爲畫一條直線只涉及兩種像素(畫筆和目標),所以這種布爾運算又稱爲「二元光柵運算」,簡稱爲「ROP2」。
在默認設備環境中,繪圖方式定義爲R2_COPYPEN,這意味着Windows只是將畫筆像素複製到目標像素代替之。
Windows定義了16種不一樣的ROP2碼,用來設置不一樣的繪圖方式。
設置繪圖方式:SetROP2(hdc, iDrawMode);
獲取繪圖方式:iDrawMode = GetROP2(hdc);
43 繪製填充區域
Windows中有7個用來畫帶邊緣的填充圖形的函數:
① Rectangle:畫矩形
② Ellipse:畫橢圓
③ RoundRect:畫帶圓角的矩形
④ Pie:畫橢圓的一部分,使其看起來像一個扇形
⑤ Chord:畫橢圓的一部分,使其看起來像弓形
⑥ Polygon:畫多邊形
⑦ PolyPolygon:畫多個多邊形
Windows用設備環境中選擇的當前畫筆來畫圖形的邊界框,邊界框還使用當前背景方式、背景色彩和繪圖方式,跟畫線時同樣。
圖形以當前設備環境中選擇的刷子來填充。默認狀況下,使用現有對象,這意味着圖形內部將畫成白色。
Windows定義6種現有刷子:WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH。
也能夠本身定義刷子 HBRUSH hBrush;
經過GetStockObject來獲取現有刷子:
hBrush = GetStockObject(WHITE_BRUSH);
經過SeletctObject將刷子選進設備環境:
SelectObject(hdc, bBrush);
若是要畫一個沒有邊界框的圖形,能夠將NULL_PEN選進設備環境。
SelectObject(hdc, GetStockObject(NULL_PEN));
若是要畫一個沒有填充內部的圖像,能夠將NULL_BRUSH選進設備環境。
SelectObject(hdc, GetStockObject(NULL_BRUSH));
畫多邊形函數的原型:
Polygon(hdc, apt, iCount);
apt參數是POINT結構的一個數組,iCount是點的數目。若是該數組中的最後一個點和第一個點不一樣,則Windows將會再加一條線,將最後一個點與第一個點連起來。
畫多個多邊形函數的原型:
PolyPolygon(hdc, apt, aiCounts, iPolyCount);
apt數組具備所有多邊形的全部點。
aiCounts數組給出了多邊形的端點數。
iPolyCount給出了所畫的多邊形的個數。
44 用畫刷填充內部
Rectangle、RoundRect、Ellipse、Chord、Pie、Polygon和PolyPolygon圖形的內部是用選進設備環境的當前畫刷來填充的。畫刷是一個8×8的位圖,它水平和垂直地重複使用來填充內部區域。
Windows有5個函數,能夠本身建立邏輯畫刷,而後用SelectObject將畫刷選進設備環境。
① hBrush = CreateSolidBrush(crColor); 純顏色刷子
② hBrush = CreateHatchBrush(iHatchStyle, crColor); 帶影射線的刷子
crColor是影線的顏色,影線的間隙用設備環境定義的背景方式和背景色來着色。
③ CreatePatternBrush()
④ CreateDIBPatternBrushPt() 基於位圖的刷子
⑤ hBrush = CreateBrushIndirect(&logbrush);
該函數包含其餘4個函數。
變量logbrush是一個類型爲LOGBRUSH的結構,該結構有三個字段
UINT lbStyle;
COLORREF lbColor;
LONG lbHatch;
45 矩形函數
Windows包含了幾種使用RECT結構和「區域」的繪圖函數。區域就是屏幕上的一塊地方,是矩形,多邊形和橢圓的組合。
FillRect(hdc, &rect, hBrush); //用指定畫刷來填充矩形。該函數不須要事先將畫刷選進設備環境。 FrameRect(hdc, &rect, hBrush); //使用畫刷畫矩形框,但不填充矩形。 InvertRect(hdc, &rect); //將矩形中全部像素反轉。
經常使用矩形函數:
① SetRect(&rect, xLeft, yTop, xRight, yBottom); 設置矩形的4個字段值。
② OffsetRect(&rect, x, y); 將矩形沿x軸和y軸移動幾個單元。
③ InflateRect(&rect, x, y); 增減矩形尺寸
④ SetRectEmpty(&rect); 將矩形各字段設爲0
⑤ CopyRect(&DestRect, &SrcRect); 將矩形複製給另外一個矩形。
⑥ IntersectRect(&DestRect, &SrcRect1,&ScrRect2);獲取兩個矩形的交集
⑦ UnionRect(&DestRect, &SrcRect1,&ScrRect2); 獲取兩個矩形的並集
⑧ bEmpty = IsRectEmpty(&rect); 肯定矩形是否爲空
⑨ binRect = PtinRect(&rect, point);肯定點是否在矩形內
46 建立和繪製區域
區域是對顯示器上一個範圍的描述,這個範圍是矩形、多邊形和橢圓的組合。
區域能夠用於繪製和剪裁,經過將區域選進設備環境,就能夠用區域來進行剪裁。
當建立一個區域時,Windows返回一個該區域的句柄,類型爲HRGN。
HRGN hRgn;
① 建立矩形區域:
hRgn = CreateRectRgn(xLeft, yTop, xRight, yBottom);
或
hRgn = CreateRectRgnIndirect(&rect);
② 建立橢圓區域:
hRgn = CreateEllipticRgn(xLeft, yTop, xRight, yBottom);
或
hRgn = CreateEllipticRgnIndirect(&rect);
③ 建立多邊形區域:
hRgn = CreatePolygonRgn(&point, iCount, iPolyFillMode);
point參數是個POINT類型的結構數組;
iCount是點的數目;
iPolyFillMode是ALTERNATE或者WINDING
④ 區域的融合
iRgnType = CombineRgn(hDestRgn, hSrcRgn1, hSrcRgn2, iCombine);
這一函數將兩個源區域組合起來並用句柄hDestRgn指向組合成的目標區域。
iCombine參數說明了hSrcRgn1和hSrcRgn2是怎麼組合的。 RGN_AND 公共部分 RGN_OR 所有 RGN_XOR 所有除去公共部分 RGN_DIFF hSrcRgn1不在hSrcRgn2的部分 RGN_COPY hSrcRgn1的所有,忽略hSrcRgn2 //區域的句柄能夠用到4個繪圖函數: FillRgn(hdc, hRgn, hBrush); FrameRgn(hdc, hRgn, xFrame, yFrame); //xFrame, yFrame是畫在區域周圍邊框的寬度和高度。 InvertRgn(hdc, hRgn); PaintRgn(hdc, hRgn);
47 矩形與區域的剪裁
區域也在剪裁中扮演了一個角色。
InvalidateRect函數使顯示的一個矩形區域失效,併產生一個WM_PAINT消息。
InvalidateRect(hwnd, NULL, TRUE); 清除客戶區;
能夠經過調用GetUpdateRect來獲取失效矩形的座標。
使用ValidateRect函數使客戶區的矩形有效。
當接收到一個WM_PAINT消息時,無效矩形的座標能夠從PAINTSTRUCT結構中獲得,該結構是用BeginPaint函數填充的。
Windows中有兩個做用於區域而不是矩形的函數:
InvalidateRgn(hwnd, hRgn, bErase);
和
ValidateRgn(hwnd, hRgn);
因此當接收到一個WM_PAINT消息時,可能由無效區域引發的。剪裁區域不必定是矩形。
SelectObject(hdc, hRgn);
或
SelectClipObject(hdc, hRgn);
經過將一個區域選進設備環境來建立本身的剪裁區域。
---------------------------------------------------------------------------------------------------------------------------
四 鍵盤
48 鍵盤基礎
Windows程序得到鍵盤輸入的方式:鍵盤輸入以消息的形式傳遞給程序的窗口過程。
Windows用8種不一樣的消息來傳遞不一樣的鍵盤事件。
Windows程序使用「鍵盤加速鍵」來激活通用菜單項。加速鍵一般是功能鍵或字母同ctrl鍵的組合。Windows將這些鍵盤加速鍵轉換爲菜單命令消息。
程序用來從消息隊列中檢索消息的MSG結構包括hwnd字段。此字段指出接收消息的窗口句柄。消息循環中的DispatchMessage函數向窗口過程發生該消息,此窗口過程與須要消息的窗口相聯繫。當按下鍵盤上的鍵時,只有一個窗口過程接收鍵盤消息,而且此消息包括接收消息的窗口句柄。
接收特定鍵盤事件的窗口具備輸入焦點。
窗口過程經過捕獲WM_SETFOCUS和WM_KILLFOCUS消息來斷定它的窗口什麼時候擁有輸入焦點。WM_SETFOCUS指示窗口正在獲得輸入焦點,WM_KILLFOCUS表示窗口正在失去輸入焦點。
當用戶按下並釋放鍵盤上的鍵時,Windows和鍵盤驅動程序將硬件掃描碼轉換爲格式消息。Windows在「系統消息隊列」中保存這些消息。系統消息隊列是單消息隊列,它由Windows維護,用於初步保存用戶從鍵盤和鼠標輸入的信息。只有當Windows應用程序處理完前一個用戶輸入消息時,Windows纔會從系統消息隊列中取出下一個消息,並放入應用程序的消息隊列。
49 擊鍵和字符
應用程序從Windows接受的關於鍵盤事件的消息能夠分爲擊鍵和字符兩類。
按下鍵是一次擊鍵,釋放鍵也是一次擊鍵。
對產生可顯示字符的擊鍵組合,Windows不只給程序發送擊鍵消息,並且還發送字符消息。有些鍵不產生字符,對於這些鍵,Windows只產生擊鍵消息。
50 擊鍵消息
當按下一個鍵時,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN消息放入有輸入焦點的窗口的消息隊列。
當釋放一個鍵時,Windows把WM_KEYUP或者WM_SYSKEYUP消息放入消息隊列。
能夠有多個KEYDOWN,但相對來講只有一個KEYUP。
經過調用GetMessageTime能夠得到按下或者釋放鍵的相對時間。
51 系統擊鍵和非系統擊鍵
WM_SYSKEYDOWN和WM_SYSKEYUP中的「SYS」表明「系統」,它表示該擊鍵對Windows比對Windows應用程序更加劇要。
程序一般能夠忽略WM_SYSKEYDOWN和WM_SYSKEYUP消息,並將它們傳送到DefWindowProc。
若是想在本身的窗口過程當中包括捕獲系統擊鍵的代碼,那麼在處理這些消息以後再傳送到DefWindowProc,Windows就仍然能夠將它們用於一般的目的。
對全部4類擊鍵消息,wParam是虛擬鍵代碼,表示按下或釋放的鍵。而lParam則包含屬於擊鍵的其餘數據。
52 虛擬鍵碼
虛擬鍵碼保存在WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP消息的wParam參數中。
53 lParam信息
在4個擊鍵消息中,wParam消息參數含有虛擬鍵碼,而lParam消息參數則含有對了解擊鍵很是有用的其餘信息。
在lParam的32位中,分爲6個字段。
0~15:重複計數;
16~23:8位OEM;
24:擴展鍵標誌;
29:環境代碼;
30:鍵的先前狀態;
31:轉換狀態。
(1)重複計數
重複計數是該消息所表示的擊鍵次數。大多數狀況下,重複計數設置爲1。
在KEYDOWN消息中,重複計數能夠大於1,表示該鍵重複n次。
在KEYUP消息中,重複計數老是1.
(2)OEM掃描碼
OEM掃描碼是由硬件(鍵盤)產生的代碼。Windows程序可以忽略幾乎全部的OEM掃描碼,除非它取決於鍵盤的物理佈局。
(3)擴展位標識
若是擊鍵結構來自IBM加強鍵盤的附加鍵之一,那麼擴展鍵標誌爲1.
(4)環境代碼
環境代碼在按下Alt鍵後爲1。對WM_SYSKEYDOWN和WM_SYSKEYUP消息,這一位老是1;對WM_KEYDOWN和WM_KEYUP消息,這一位老是0;
可是有2個例外:
① 若是活動窗口最小化了,則它沒有輸入焦點。這時候全部的擊鍵都產生WM_SYSKEYDOWN和WM_SYSKEYUP消息。
② 若是Alt鍵未按下,則環境代碼域被設置爲0.
(5)鍵的先前狀態
若是在此以前鍵是釋放的,則鍵的先前狀態爲0,不然爲1.
對WM_KEYUP或者WM_SYSKEYUP消息,它老是設置爲1.
對WM_KEYDOWN和WM_SYSKEYDOWN消息,此位能夠是1,也能夠是0.
(6)轉換狀態
若是鍵正在被按下,則轉換狀態爲0;
若是鍵正在被釋放,則轉換狀態爲1.
對WM_KEYDOWN和WM_SYSKEYDOWN消息,此域爲0;
對WM_KEYUP或者WM_SYSKEYUP消息,此域爲1.
54 換擋狀態
在處理擊鍵消息是,可能須要知道是否按下了換擋鍵(Shift, Ctrl, Alt)或開關鍵(Caps Lock, Num Lock, Scroll Lock)。經過調用GetKeyState函數,就能得到此信息。
int iState;
iState = GetKeyState(VK_SHIFT);
若是按下了Shift,則iState值爲負。(高位被置1)。
55 字符消息
在WinMain中,有這樣一個消息循環:
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } /* GetMessage函數用隊列中的下一個消息填充msg結構的字段。 DispatchMessage以此消息爲參數調用適當的窗口過程。 TranslateMessage函數將擊鍵消息轉換爲字符消息。若是消息爲WM_KEYDOWN或WM_SYSKEYDOWN,而且擊鍵與換擋狀態相結合產生一個字符,
則TranslateMessage把字符消息放入消息隊列中。此字符消息將是GetMessage從消息隊列中獲得的擊鍵消息以後的下一個消息。 */
56 四類字符消息
字符消息能夠分爲四類:WM_CHAR、WM_DEADCHAR和WM_SYSCHAR、WM_SYSDEADCHAR。
其中,WM_CHAR、WM_DEADCHAR是從WM_KEYDOWN消息獲得的。WM_SYSCHAR、WM_SYSDEADCHAR是從WM_SYSKEYDOWN消息獲得的。
大多數狀況下,Windowns程序會忽略除WM_CHAR消息以外的任何消息。
伴隨四個字符消息的lParam參數與產生字符代碼消息的擊鍵消息的lParam參數相同。不過,參數wParam不是虛擬鍵碼,而是ANSI或Unicode字符代碼。
57 消息順序
由於TranslateMessage函數從WM_KEYDOWN和WM_SYSKEYDOWN消息產生了字符消息,因此字符消息是夾在擊鍵消息之間傳遞給窗口過程的。
(1)若是按下A鍵,再釋放A鍵,將產生3個消息。
① WM_KEYDOWN 「A」的虛擬鍵碼(0x41)
② WM_CHAR 「a」的字符代碼(0x61)
③ WM_KEYUP 「A」的虛擬鍵碼(0x41)
(2)若是按下Shift鍵和A鍵,而後釋放,將產生5個消息。
① WM_KEYDOWN 虛擬鍵碼VK_SHIFT
② WM_KEYDOWN 「A」的虛擬鍵碼(0x41)
③ WM_CHAR 「a」的字符代碼(0x61)
④ WM_KEYUP 「A」的虛擬鍵碼(0x41)
⑤ WM_KEYUP 虛擬鍵碼VK_SHIFT
(3)若是按住A鍵不釋放,將自動重複產生一系列的擊鍵,那麼對每一個WM_KEYDOWN消息,就會獲得一個字符消息。
① WM_KEYDOWN 「A」的虛擬鍵碼(0x41)
② WM_CHAR 「a」的字符代碼(0x61)
③ WM_KEYDOWN 「A」的虛擬鍵碼(0x41)
④ WM_CHAR 「a」的字符代碼(0x61)
⑤ WM_KEYDOWN 「A」的虛擬鍵碼(0x41)
⑥ WM_CHAR 「a」的字符代碼(0x61)
⑦ WM_KEYUP 「A」的虛擬鍵碼(0x41)
58 處理控制字符
處理擊鍵和字符消息的基本規則是:若是須要讀取輸入到窗口的鍵盤字符,那麼能夠處理WM_CHAR消息。若是須要讀取光標鍵、功能鍵、Delete鍵、Insert鍵、Shite鍵、Ctrl鍵、以及Alt鍵,那麼能夠處理WM_KEYDOWN消息。
對於刪除、製表、回車、退出鍵,它們產生WM_CHAR消息和WM_KEYDOWN消息,應該將這些按鍵處理成控制字符而不是虛擬鍵碼。
case WM_CHAR: { case ‘\b’: 刪除鍵← ….. break; case ‘\t’: 製表鍵tab ….. break; case ‘\n’: 回車 ….. break; case ‘\r’: 換行 ….. break; default: ….. break; }
59 插入符函數(鼠標閃爍)
主要有8個插入符函數:
① CreateCaret 建立與窗口相關的插入符;
② SetCaret 在窗口中設置插入符的位置
③ ShowCaret 顯示插入符
④ HideCaret 隱藏插入符
⑤ DestroyCaret 撤銷插入符
⑥ GetCaretPos 獲取插入符位置
⑦ GetCaretBlinkTime 獲取插入符閃爍時間
⑧ SetCaretBlinkTime 設置插入符閃爍時間
在Windows中,插入符定義爲水平線、與字符大小相同的方框,或者與字符等高的豎線。
只有當窗口有輸入焦點時,窗口內顯示插入符纔有意義。
經過處理WM_SETFOCUS和WM_KILLFOCUS消息,程序就能夠肯定它是否有輸入焦點。窗口過程在有輸入焦點的時候接受WM_SETFOCUS消息,失去輸入焦點的時候接受WM_KILLFOCUS消息。
使用插入符的規則:窗口過程在WM_SETFOCUS消息期間調用CreateCaret,在處理WM_KILLFOCUS消息期間調用DestroyCaret。
插入符剛建立的時候是隱蔽的,必須顯式使用ShowCaret函數將插入符設爲可見。
當窗口過程處理一個非WM_PAINT消息並且但願在窗口內繪製某些東西時,必須調用HideCaret隱藏插入符。在繪製完畢以後,再調用ShowCaret顯式插入符。
---------------------------------------------------------------------------------------------------------------------------
五 鼠標
60 鼠標基礎
用GetSystemMetrics函數來肯定鼠標是否存在:
fMouse = GetSystemMetrics(SM_MOUSEPRESENT);
要肯定所安裝鼠標上鍵的個數,可以使用:
cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS); //若是沒有安裝鼠標,返回0.
當Windows用戶移動鼠標時,Windows在顯示屏上移動一個稱爲「鼠標光標」的小位圖。鼠標光標有一個指向顯示屏上精確位置的單像素的「熱點」。
Windows支持幾種預約義的鼠標光標,程序可使用這些光標。最多見的是稱爲IDC_ARROW的斜箭頭。熱點在箭頭的頂端。IDC_CROSS光標的熱點在十字交叉線的中心。IDC_WAIT光標是一個沙漏,用於指示程序正在運行。
鼠標鍵動做的術語:
① 單擊 按下並放開一個鼠標鍵
② 雙擊 快速按下並放開鼠標鍵兩次
③ 拖曳 按住鼠標鍵並移動鼠標
對於三鍵鼠標,三個鍵分別被稱爲左鍵、中鍵、右鍵。在Windows頭文件中定義的與鼠標有關的標識符使用縮寫LBUTTON、MBUTTON、RBUTTON。
61 客戶區鼠標消息
Windows只把鍵盤消息發送給擁有輸入焦點的窗口。鼠標消息與此不一樣,只要鼠標跨越窗口或者在某窗口中按下鼠標鍵,那麼窗口過程就會收到鼠標消息,而無論該窗口是否活動或者擁有輸入焦點。
當鼠標移過窗口的客戶區時,窗口過程收到WM_MOUSEMOVE消息。當在窗口的客戶區按下或者釋放一個鼠標鍵時,窗口過程會收到以下消息:
鍵:
按下
釋放
雙擊鍵
左:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
中:
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MBUTTONDBLCLK
右:
WM_RBUTTONDOWN
WM_RBUTTONUP
WM_RBUTTONDBLCLK
僅當定義的窗口類能接收DBLCLK消息以後,窗口過程才能接收到雙擊消息。
對於全部這些消息來講,其lParam值均含有鼠標的位置:低位是x座標,高位是y座標。這兩個座標是相對於窗口客戶區左上角的位置。
wParam的值指示鼠標鍵和Shift和Ctrl鍵的狀態。MK_前綴表明「鼠標鍵」。
MK_LBUTTON 按下左鍵
MK_MBUTTON 按下中鍵
MK_RBUTTON 按下右鍵
MK_SHIFT 按下Shift鍵
MK_CONTROL 按下Ctrl鍵
若是接收到WM_LBUTTONDOWN消息,並且值wParam & MK_SHIFT是TRUE,就知道當左鍵按下時,也按下了右鍵。
當把鼠標移過窗口的客戶區時,Windows並不爲鼠標的每一個可能的像素位置都產生一條WM_MOUSEMOVE消息。程序接收到WM_MOUSEMOVE消息的次數取決於鼠標硬件以及窗口過程處理鼠標移動消息的速度。
62 處理Shift鍵
要判斷鼠標移動時,是否按下了Shift鍵,能夠經過wParam進行判斷,具體方法以下:
if (wParam & MK_SHIFT) { if (wParam & MK_CONTROL) { //按下了Shift和Ctrl鍵 } else { //按下了Shift鍵 } }
63 雙擊鼠標鍵
要肯定爲雙擊,這兩次單擊必須發生在其相互的物理位置十分接近的情況下,默認時範圍是一個平均系統字體字符的寬,半個字符的高,而且發生在指定的時間間隔內。
若是但願窗口過程可以接收到雙擊鍵的鼠標消息,那麼在調用RegisterClass初始化窗口類結構時,必須在窗口風格中包含CS_DBCLKS標識符。
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBCLKS;
若是在窗口風格中未包含CS_DBCLKS,那麼用戶在短期內雙擊了鼠標鍵,窗口過程將接收到以下消息:
WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDOWN WM_LBUTTONUP //若是窗口類風格中包含了CS_DBCLKS,那麼雙擊鼠標鍵時窗口過程將接收到以下消息: WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBCLK WM_LBUTTONUP
64 非客戶區鼠標消息
在窗口的客戶區內移動或按下鼠標鍵時,將產生10個消息。若是鼠標在窗口的客戶區以外,但在窗口內,Windows將給窗口過程發生一個「非客戶區」鼠標消息。窗口非客戶區包括標題欄、菜單、和窗口滾動條。
一般不須要處理非客戶區鼠標消息,而將這些消息傳給DefWindowProc,從而使Windows執行系統功能。
消息中包含字母「NC」以表示是非客戶區消息。
若是鼠標在窗口的非客戶區移動,那麼窗口過程接收到WM_NCMOUSEMOVE消息。其餘動做產生以下消息:
鍵
按下
釋放
雙擊
左
WM_NCLBUTTONDOWN
WM_NCLBUTTONUP
WM_NCLBUTTONDBCLK
中
WM_NCMBUTTONDOWN
WM_NCMBUTTONUP
WM_NCMBUTTONDBCLK
右
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
WM_NCRBUTTONDBCLK
非客戶區鼠標消息的wParam和lParam參數意義以下:
wParam:指明移動或者單擊鼠標鍵的非客戶區位置。以HT開頭。
lParam:包含低位字的x座標和高位字的y座標,可是它們都是屏幕座標,而不是客戶區座標。
//使用如下兩個函數將屏幕座標和客戶區座標互換。 ScreenToClient(hwnd, &pt); ClientToScreen(hwnd, &pt);
若是屏幕座標點在窗口客戶區的上面或者左邊,客戶區座標x或y值就是負值。
65 命中測試
WM_NCHITTEST表明「非客戶區命中測試」。此消息優先於全部其餘的客戶區和非客戶區鼠標消息。lParam參數含有鼠標位置的x和y屏幕座標。wParam參數沒有用。
Windows應用程序一般把這個消息傳送給DefWindowProc,而後Windows用WM_NCHITTEST消息產生基於鼠標位置的全部其餘鼠標消息。對於非客戶區鼠標消息,在處理WM_NCHITTEST消息時,從DefWindowProc返回的值將成爲鼠標消息中的wParam參數,這個值能夠是任意非客戶區鼠標消息的wParam值再加上如下內容:
HTCLIENT 客戶區
HTNOWHERE 不在窗口中
HTTRANSPARENT 窗口由另外一個窗口覆蓋
HTERROR 使DefWindowProc產生蜂鳴聲
若是DefWindowProc在其處理WM_NCHITTEST消息後返回HTCLIENT,那麼Windows將把屏幕座標轉換爲客戶區座標併產生客戶區鼠標消息。
66 從消息產生消息
若是在一個Windows程序的系統菜單圖標上雙擊一下,那麼程序將會終止。雙擊產生一些了的WM_NCHITTEST消息。因爲鼠標定位在系統菜單圖標上,因此DefWindowProc將返回HTSYSMENU的值,而且Windows把wParam等於HTSYSMENU的WM_NCLBUTTONDBCLK消息放在消息隊列中。
當DefWindowProc接收到wParam參數爲HTSYSMENU的WM_NCLBUTTONDBCLK消息時,就把wParam參數爲SC_CLOSE的WM_SYSCOMMAND消息放入消息隊列中。一樣,窗口過程也把這個消息傳給DefWindowProc。DefWindowProc經過給窗口過程發生WM_CLOSE消息來處理該消息。
若是一個程序在終止前須要來自用戶的確認,那麼窗口過程就必須捕獲WM_CLOSE,不然,DefWindowProc將調用DestroyWindow函數來處理WM_CLOSE。
67 捕獲鼠標
捕獲鼠標,只要調用SetCapture(hwnd);在這個函數調用以後,Windows將全部鼠標消息發給窗口句柄爲hwnd的窗口過程。鼠標消息老是客戶區消息,即便鼠標正在窗口的非客戶區。lParam參數將指示鼠標在客戶區座標中的位置。不過,當鼠標位於客戶區的左邊或者上方的時候,這些x和y座標能夠是負的。
當須要釋放鼠標時,調用ReleaseCapture();就可恢復正常。
若是鼠標被捕獲,而鼠標鍵並無被按下,而且鼠標光標移到了另外一個窗口上,那麼將不是由捕獲鼠標的那個窗口而是由光標下面的窗口來接收鼠標消息。
68 鼠標輪
鼠標輪的轉動產生一個WM_MOUSEWHEEL消息。
lParam參數將得到鼠標的位置,座標是相對於屏幕左上角的,不是客戶區的。
wParam參數低字包含一系列的標識,用於代表鼠標鍵和Shift與Ctrl鍵的狀態。
wParam的高字中有一個「delta」值,該值默承認以是120或-120,這取決於滾輪是向前轉動仍是向後轉動。值120或-120代表文檔將分別向上或向下滾動三行。
---------------------------------------------------------------------------------------------------------------
六 計時器
69 計時器基礎
計時器是一種輸入設備,它週期性地每通過一個指定的時間間隔就用WM_TIMER消息通知應用程序一次。
能夠經過調用SetTimer函數爲Windows應用程序分配一個計時器。SetTimer有一個時間間隔範圍爲1~4294967295毫秒的整型參數,這個值指示Windows每隔多長時間給程序發送WM_TIMER消息。
當程序用完計時器時,就調用KillTimer函數中止計時器消息。
KillTimer調用清除消息隊列中還沒有被處理的WM_TIMER消息,從而使程序在調用KillTimer以後就不會再受到WM_TIMER消息。
70 系統和計時器
Windows計時器是PC的硬件和ROM BIOS構造的計時器邏輯的一種相對簡單的擴展。
BIOS的「計時器滴答」中斷約每54.915毫秒或者大約每秒18.2次。
在Microsoft Windows NT中,計時器的分辨率爲10毫秒。
Windows應用程序不能以高於這個分辨率的速率接收WM_TIMER消息。在SetTimer調用中指定的時間間隔老是截尾爲時鐘滴答的整數倍。例如,1000毫秒的間隔除以54.925毫秒≈18.207個時鐘滴答,截尾後爲18個時鐘滴答,它其實是989毫秒。對每一個小於55毫秒的間隔,每一個時鐘滴答都產生一個WM_TIMER消息。
71 計時器消息不是異步的
計時器是基於硬件計時器中斷。但WM_TIMER消息卻不是異步的。
WM_TIMER消息放在正常的消息隊列之中,和其餘消息一塊兒參加排序,所以,若是在SetTimer調用中指定間隔爲1000毫秒,那麼不能保持程序每1000毫秒就會收到一個WM_TIMER消息。若是其餘程序的運行事件超過一秒,在此期間,程序將不會收到任何WM_TIMER消息。
Windows不能持續向消息隊列放入多個WM_TIMER消息,而是將多餘的WM_TIMER消息合併成一個消息。
72 計時器的使用(方法一)
若是須要在整個程序期間使用計時器,那麼能夠在處理WM_CREATE消息時調用SetTimer,並在處理WM_DESTROY消息時調用KillTimer。
SetTimer函數以下所示:
SetTimer(hwnd, 1, uiMsecInterval, NULL); /* 第一個參數是其窗口過程將接收WM_TIMER消息的窗口的句柄; 第二個參數是計時器ID,只要是非0的整數就能夠,若是設置多個計時器,那麼各個計時器的ID應該不一樣。 第三個參數是一個32位無符號整數,以毫秒爲單位指定一個時間間隔。 第四個參數是回調函數的地址,若是處理WM_TIMER消息不是回調函數,那麼設置爲NULL。 */ //KillTimer函數調用以下: KillTimer(hwnd, 1); // 第一個參數是其窗口過程將接收WM_TIMER消息的窗口的句柄; //第二個參數是計時器ID。 //KillTimer用於在任什麼時候刻中止WM_TIMER消息。
當窗口過程收到一個WM_TIMER消息時,wParam參數等於計時器的ID值,lParam參數爲0.若是須要設置多個計時器,那麼對每一個計時器都使用不一樣的計時器ID。wParam的值將隨傳遞到窗口過程的WM_TIMER消息的不一樣而不一樣。
73 計時器的使用(方法二)
第一種方法是把WM_TIMER消息發送到一般的窗口過程。
第二種方法是讓Windows直接將計時器消息發送給程序的另外一個函數。
接收這些計時器消息的函數稱爲「回調函數」,這是一個在程序之中,可是由Windows而不是程序自己調用的函數。先告訴Windows這個函數的地址,而後Windows調用此函數。
窗口過程其實就是一種回調函數。回調函數必須定義爲CALLBACK,由於它是由Windows從程序的代碼段調用的。回調函數的參數和回調函數的返回值依賴於回調函數的目的。同計時器相關的回調函數中,輸入參數同學口過程的輸入參數同樣。計時器回調函數不向Windows返回值,能夠設置爲VOID。
假設計時器回調函數稱爲TimerProc,那麼能夠定義以下:
VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
WM_TIMER消息的處理過程;
}
TimerProc的參數hwnd是在調用SetTimer時指定的窗口句柄。Windows只把WM_TIMER消息消息送過TimerProc,所以消息參數老是等於WM_TIMER。iTimerID值是計時器ID,dwTimer值是與從GetTickCount函數的返回值兼容的值,是反映Windows啓動後所通過的毫秒數。
在使用回調函數處理WM_TIMER消息時,窗口過程當中設置SetTimer的第4個參數由回調函數的地址取代,以下所示:
SetTimer(hwnd, iTimerID, iMsecInterval, TimerProc);
回調函數必須和窗口過程函數同樣,一塊兒被聲明在程序的開始處,像TimerProc的函數聲明以下:
VOID CALLBACK TimerProc(HWND, UINT, UINT, DWORD);
74 計時器的使用(方法三)(少用)
設置計時器的第三種方法相似於第二種方法,只是傳遞給SetTimer的hwnd參數被設置爲NULL,而且第二個參數計時器ID也被忽略,設置爲0,最後才函數返回計時器ID。
iTimerID = SetTimer(NULL, 0, iMsecInterval, TimerProc);
若是沒有可用的計時器,那麼從SetTimer返回的iTimerID值將爲NULL。
KillTimer的第一個參數也必須是NULL,計時器ID必須是SetTimer的返回值。
傳遞給TimerProc計時器函數的hwnd參數也必須是NULL。
75 獲取當前時間
首先介紹下SYSTEMTIME結構,以下所示:
typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, *PSYSTEMTIME;
SYSTEMTIME結構包含日期和時間。月份由1開始遞增,星期由0開始遞增(星期天是0)。wDay是本月的當前日子,從1開始遞增。
SYSTEM主要用於GetLocalTime和GetSystemTime函數。
GetSystemTime函數返回當前的世界時間(格林尼治時間)
GetSystemTime函數返回當地時間。
76 WM_SETTINGCHANGE消息
若是用戶改變了任何系統設置,Windows會產生WM_SETTINGCHANGE消息,並傳送給全部的應用程序。
------------------------------------------------------------------------------------------------------------------------
七 子窗口控制
77 子窗口控制概述
當子窗口的狀態發生改變時,子窗口處理鼠標和鍵盤消息並通知父窗口。子窗口這時就變成了其父窗口高一級的輸入設備。
能夠創建本身的子窗口控制,也能夠利用一些預約義的窗口類和窗口過程來創建標準的子窗口控制。
子窗口控制採用的形式有:按鈕、複選框、編輯框、列表框、組合框、文本串、滾動條。
子窗口控制在對話框中最經常使用。
子窗口控制的位置和尺寸是在程序的資源描述文中的對話框模板中定義的。也可使用預約義的、位於普通窗口客戶區表面的子窗口控制。能夠調用一次CreateWindow來創建一個子窗口,並經過調用MoveWindow來調整子窗口的位置和尺寸。父窗口過程向子窗口控制發送消息,子窗口控制向父窗口過程返回消息。
78 按鈕類
按鈕屬於窗口。
按鈕窗口風格都以字母BS開頭,它表明「按鈕風格」。按鈕使用CreateWindow建立。
HWND hwndButton; hwndButton = CreateWindow{ /* 參數1 ClassName 類名, 參數2 Window text 按鈕顯示的文本, 參數3 Window Style 窗口風格 有WS_CHILD | WS_VISIBLE | 按鈕種類 參數4 x位置 參數5 y位置 說明按鈕左上角相對於父窗口客戶區左上角的位置 參數6 Width 寬度 按鈕寬度 參數7 Height 高度 按鈕的高度 參數8 父窗口句柄 參數9 子窗口ID(強制HMENU類型) 參數10 實例句柄((LPCREATESTRUCT)lParam -> hInstance) 參數11 額外參數(通常爲NULL) */ }
注:參數3中的按鈕種類分爲:
下壓按鈕:BS_PUSHBUTTON、BS_DEFPUSHBUTTON 下壓按鈕
複選框:BS_CHECKBOX、BS_AUTOCHECKBOX
三狀態複選框:BS_3STATE、BS_AUTO3STATE
單選按鈕:BS_RADIOBUTTON、BS_AUTORADIOBUTTON
組合框:BS_GROUPBOX
自定義按鈕:BS_OWNEDRAW
參數9的子窗口ID對於每一個子窗口都不一樣。在處理來自子窗口的WM_COMMAND消息時,ID幫助窗口過程識別出相應的子窗口。子窗口ID必須被強制轉換爲HMENU。
參數10的實例句柄利用了以下事實:在處理WM_CREATE消息的過程當中,lParam其實是指向CREATESTRUCT結構的指針,該結構有一個hInstance成員。
也可使用GetWindowLong(hwnd, GWL_HINSTANCE)函數調用來獲取實例句柄。
79 子窗口向父窗口發送消息
當用鼠標點擊按鈕時,子窗口控制就向其父窗口發送一個WM_COMMAND消息。父窗口過程捕獲WM_COMMAND消息,其wParam和lParam消息參數含義以下:
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知碼
lParam 子窗口句柄
子窗口ID是在建立子窗口時傳遞給CreateWindow的值。
通知碼詳細代表了消息的含義。
當用鼠標單擊按鈕時,該按鈕文本的周圍會有虛線。這代表該按鈕擁有了輸入焦點,全部鍵盤輸入都將傳送給子窗口按鈕控制。而後按鈕即便擁有輸入焦點,也只能處理空格鍵。
80 父窗口向子窗口發送消息
父窗口能夠給子窗口發送消息,這些消息包括之前綴爲WM開頭的許多消息。另外,還有8個按鈕說明消息,以BM開頭。
BM_GETCHECK:獲取複選框和單選按鈕的選中標記
BM_SETCHECK:設置複選框和單選按鈕的選中標記
BM_GETSTATE:獲取按鈕狀態(正常仍是按下)
BM_SETSTATE:設置按鈕狀態
BM_GETIMAGE
BM_SETIMAGE
BM_CLICK
BM_SETSTYLE:容許在按鈕建立後改變按鈕風格
每一個子窗口控制都具備一個在其兄弟中惟一的窗口句柄和ID值,對於句柄和ID值這二者,知道其中一個就能夠得到另外一個。
id = GetWindowLong(hwndChild, GWL_ID);
hwndChild = GetDlgItem(hwndParent, id);
81 下壓按鈕
下壓按鈕控制主要用來觸發一個當即響應的動做,而不保**何開關指示。有兩種類型的按鈕控制窗口風格。分別是BS_PUSHBUTTON和BS_DEFPUSHBUTTON。
當用來設計對話框時,兩種風格做用不一樣,但當用做子窗口控制時,兩種類型的按鈕做用相同。
當按鈕的高度爲文本字符高度的7/4倍時,按鈕的外觀最好。
按鈕的文本尺寸除了以前介紹的從TEXTMETRIC結構中獲取以外,更簡便的方法是經過GetDialogBaseUnits函數來得到默認字體字符的高度和寬度。
此函數返回一個32位的值,其中低字位表示寬度,高字位表示高度。
當鼠標在按鈕中按下時,按鈕使用三維陰影重畫本身。
當鼠標放開時,就恢復按鈕緣由,並向父窗口發送一個WM_COMMAND消息和BN_CLICKED通知碼。
82 複選框
複選框是一個文本框,文本一般出如今複選框的右邊。複選框一般用於容許用戶對選項進行選擇的應用程序中。複選框的經常使用功能如同一個開關:單擊一次顯示覆選標記,再次單擊清除複選標記。
複選框最經常使用的2種風格是BS_CHECKBOX和BS_AUTOCHECKBOX。在使用BS_CHECKBOX時,須要本身向該控制發送BM_SETCHECK消息來設置複選標記。wParam參數置1時設置複選標記,置0時清除複選標記。經過向該控制發送BM_GETCHECK消息,能夠獲得該複選框的當前狀態。
能夠用以下指令來翻轉複選標記:
SendMessage((HWND)lParam, BM_SETCHECK, (WPARAM) !SendMessage((HWND)lParma, BM_GETCHECK, 0, 0), 0);
對於BS_AUTOCHECKBOX風格,按鈕本身觸發複選標記的開和關,窗口過程能夠忽略WM_COMMAND消息。當須要知道按鈕的當前狀態時,能夠向控制發送BM_GETCHECK消息:
iCheck = (int)SendMessage(hwndButton, BM_GETCHECK, 0, 0);
若是按鈕被選中,則iCheck返回非0值。
其他兩種複選框風格是BS_3STATE和BS_AUTO3STATE,這兩種風格能顯示第三種狀態——複選框內是灰色的——它出如今向控制發送wParam = 2的WM_SETCHECK消息時。
83 單選按鈕
單選按鈕的形狀是個圓圈。圓圈內的加劇圓點表示該單選按鈕已經被選中。單選按鈕有窗口風格BS_RADIOBUTTON或BS_AUTORADIOBUTTON兩種,後者只用於對話框。
當收到來自單選按鈕的WM_COMMAND消息時,應該向它發送wParam等於1的BM_SETCHECK消息來顯示其選中狀態:
SendMessage(hwndButton, BM_SETCHECK, 1, 0);
對相同組中的其餘全部單選按鈕,能夠經過向它們發送wParam等於0的BM_SETCHECK消息來顯示其未選中狀態。
SendMessage(hwndButton, BM_SETCHECK, 0, 0);
84 分組框
分組框即風格爲BS_GROUPBOX的選擇框,它不處理鼠標輸入和鍵盤輸入,也不向其父窗口發送WM_COMMAND消息。分組框是一個矩形框,窗口文本在其頂部顯示。分組框經常使用來包含其餘的按鈕控制。
85 更改按鈕文本
能夠經過調用SetWindowText來更改按鈕內的文本:
SetWindowText(hwnd, pszString);
hwnd是窗口句柄,pszString是一個指向NULL終結串的指針。
對於通常的窗口,更改的是窗口的標題欄文本,對於按鈕控制來講,更改的是按鈕的顯示文本。
能夠獲取窗口的當前文本:
iLength = GetWindowText(hwnd, pszBuffer, iMaxLength);
iMaxLength指定複製到pszBuffer指向的緩衝區中的最大字符數,該函數返回複製的字符數。
能夠經過調用
iLength = GetWindowTextLength(hwnd);獲取文本的長度。
86 可見和啓用的按鈕
爲了接受鼠標和鍵盤輸入,子窗口必須是可見的和被啓用的。當窗口是可見的可是非啓用時,窗口以灰色顯示正文。
若是在創建子窗口時,沒有將WS_VISIBLE包含在窗口類中,那麼直到調用
ShowWindow(hwndChild, SW_SHOWNORMAL);
時子窗口才被顯示出來。
調用ShowWindow(hwndChild, SW_HIDE);
將子窗口隱藏起來。
使用EnableWindow(hwndChild, TRUE)來啓用窗口。
87 按鈕和輸入焦點
當Windows將輸入焦點從一個窗口轉換到另外一個窗口時,首先給正在失去輸入焦點的窗口發送一個WM_KILLFOCUS消息,wParam參數是接收輸入焦點的窗口的句柄。而後,Windows向正在接收輸入焦點的窗口發送一個WM_SETFOCUS消息,同時wParam參數是正在失去輸入焦點的窗口的句柄。
能夠經過調用SetFocus來恢復輸入焦點,如:
case WM_KILLFOCUS: if (hwnd == GetParent((HWND)wParam)) SetFoucs(hwnd); return 0;
88 靜態類
在CreateWindow函數中指定窗口類爲「static」,就能夠創建靜態的子窗口控制。這些子窗口既不接收鼠標或鍵盤輸入,也不向父窗口發送WM_COMMAND消息。
當在靜態子窗口上移動或按下鼠標時,這個子窗口將捕獲WM_NCHITTEST消息,並將HTTRANSPARENT的值返回給Windows,這將使Windows向其下層窗口發送相同的WM_NCHITTEST消息。
89 滾動條類
滾動條類是能夠在父窗口的客戶區的任何地方出現的子窗口。可使用預先定義的窗口類「scrollbar」以及兩個滾動條風格SBS_VERT和SBS_HORZ中的一個來創建子窗口滾動條控制。
與按鈕控制不一樣,滾動條控制不向父窗口發送WM_COMMAND消息,而是像窗口滾動條同樣發送WM_VSCROLL和WM_HSCROLL消息。在處理滾動條消息時,能夠經過lParam參數來區分開窗口滾動條與滾動條控制。對於窗口滾動條,其值是0.對於滾動條控制,其值是滾動條窗口句柄。對於窗口滾動條和滾動條控制來講,wParam參數的高位字和低位字的含義相同。
窗口滾動條有固定的寬度,但滾動條控制能夠本身修改尺寸。使用CreateWindow調用時,給出矩形尺寸來肯定滾動條控制的尺寸。
若是想創建與窗口滾動條相同的滾動條控制,那麼可使用GetSystemMetrics獲取水平滾動條的高度GetSystemMetrics(SM_CYHSCROLL)或者垂直滾動條的寬度GetSystemMetrics(SM_CXVSCROLL);
滾動條窗口風格標識符SBS_LEFTALIGN、SBS_RIGHTALIGN、SBS_TOPALIGN和SBS_BUTTOMALIGN給出滾動條的標準尺寸,可是這些風格只在對話框中對滾動條有效。
對於滾動條控制,可使用以下調用來設置範圍和位置:
SetScrollRange(hwndScroll, SB_CTL, iMin, iMax, bRedraw)
SetScrollPos(hwndScroll, SB_CTL,iPos, bRedraw)
SetScrollInfo(hwndScroll, SB_CTL, &si, bRedraw)
滾動條兩端按鈕之間較大的區域顏色是有COLOR_BTNFACE和COLOR_BTNHIGHLIGHT一塊兒來肯定的。
若是捕獲了WM_CTLCOLORSCROLLBAR消息,那麼能夠在消息處理中返回畫刷以取代該顏色。
90 窗口子類化
通常咱們的消息都是傳給Windows程序的WndProc窗口過程的,可是當窗口內有子窗口控制時,咱們能夠給這個子窗口設置一個新的窗口過程,這個技術叫作「窗口子類化」。它能讓咱們給現存的窗口過程(新的)設置「鉤子」,以便在程序中處理一些消息,同時將其餘全部消息傳遞給舊的窗口過程(WndProc)。
Win32的子類化的原理是靠攔截Windows系統中的某些消息來本身進行處理,而不是交給WndProc或DefWindowProc。
將GWL_WNDPROC標識符做爲參數來調用GetWindowLong,能夠獲得這個窗口過程的地址。
能夠調用SetWindowLong給子窗口設置一個新的窗口過程。
能夠用函數指針的辦法,將咱們感興趣的消息攔截下來,處理完以後再讓預約義的窗口過程處理。這個過程大體以下:
WNDPROC OldProc;(用來保存舊的WndProc窗口過程)
OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (LONG)NewProc);
固然,這裏的新窗口過程NewProc是預先由你實現好的。上述代碼執行之後,系統在處理hwnd的窗口消息時,就會先進入你實現的NewProc回調過程,而後在處理過你感興趣的消息以後,經過CallWindowProc函數和你預先保存的OldProc再次回到原來的回調過程當中完成剩餘的工做。
91 編輯類
當創建子窗口時,CreateWindow第一個參數,即類名使用「edit」,根據CreateWindow調用中的x位置、y位置、寬度、高度等這些參數定義了一個矩形。此矩形含有可編輯文本。當子窗口控制擁有輸入焦點時,能夠輸入文本,移動光標,使用鼠標或者Shift鍵與一個光標鍵來選取部分文本,也能夠刪除、剪切、複製、粘帖文本。
92 編輯類風格
① 是WS_CHILD風格。
② 是編輯控制中的文本對齊方式,能夠左對齊ES_LEFT、右對齊ES_RIGHT、居中ES_CENTER。
③ 編輯控制是單行文本仍是多行文本,默認是單行,若是要處理回車鍵,須要增長風格ES_MULTILINE。
④ 滾動條功能,縱向是ES_AUTOVSCROLL,橫向是ES_AUTOHSCROLL。
⑤ 邊框,默認是沒邊框的,可使用風格WS_BORDER。
93 編輯控制通知
編輯控制給父窗口過程發生WM_COMMAND消息,wParam和lParam參數和按鈕控制同樣。
LOWORD(wParam) 子窗口ID
HIWORD(wParam) 通知碼(EN開頭)
lParam 子窗口句柄
通知碼以下所示:
EN_SETFOCUS 得到輸入焦點
EN_KILLFOCUS 失去輸入焦點
EN_CHANGE內容將改變
EN_UPDATE 內容已經改變
EN_ERRSPACE 輸入的文本超過30000個字符
EN_MAXTEXT 插入以後的文本超過30000個字符
EN_HSCROLL 編輯控制的水平滾動條被單擊
EN_VSCROLL 編輯控制的垂直滾動條被單擊
94 發送給編輯控制的消息
發送給編輯控制的消息運行剪切、複製、清除當前的選擇。用戶使用鼠標或Shift鍵減小光標控制鍵選擇文本並進行上面的操做。
剪切:SendMessage(hwndEdit, WM_CUT, 0, 0); 複製:SendMessage(hwndEdit, WM_COPY, 0, 0); 清除;SendMessage(hwndEdit, WM_CLEAR, 0, 0); 粘帖:SendMessage(hwndEdit, WM_PASTE, 0, 0);
//獲取當前選中文本的起始位置和末尾位置: SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd); //末尾位置實際是最後一個選擇字符的位置加1。 //選擇文本: SendMessage(hwndEdit, EM_SETSEL, iStart, iEnd); //文本置換: SendMessage(hwndEdit, EM_REPLACESEL, 0, (LPARAM)szString); //獲取多行文本的行數: iCount = SendMessage(hwndEdit, EM_GETLINECOUNT, 0, 0); //對任何特定的行,能夠獲取距離編輯緩衝區文本開頭的偏移量: iOffset = SendMessage (hwndEdit, EM_LINEINDEX, iLine, 0); //其中,行數從0開始計算,iLine值爲-1時返回包含光標所在行的偏移量。 //獲取行的長度: iLength = SendMessage (hwndEdit, EM_LINELENGTH, iLine, 0); //將一行復制到一個緩衝區: iLength = SendMessage (hwndEdit, EM_GETLINE, iLine, (LPARAM) szBuffer) ;
95 列表框類
列表框也屬於子窗口控制。列表框是文本串的集合,這些文本串是一個矩形中能夠滾動顯示的列狀列表。程序經過向列表框窗口過程發送消息,能夠在列表中增長或者刪除串。當列表框中的某項被選定時,列表框控制就向其父窗口發送WM_COMMAND消息,父窗口也就能夠肯定選定是哪一項。
列表框能夠是單選的,也能夠是多選的。選定的項被加亮顯示,而且是反顯的。
在單項選擇的列表框中,用戶按空格鍵就能夠選定光標所在位置的項。方向鍵移動光標和當前選擇指示,而且可以滾動列表框的內容。
96 列表框風格
當使用CreateWindow創建列表框子窗口時,應該將「listbox」做爲窗口類,將WS_CHILD做爲窗口風格。可是,這個默認列表框風格不向其父窗口發送WM_COMMAND消息。因此,通常都要包括列表框風格標識符LBS_NOTIFY。它容許父窗口接收來自列表框的WM_COMMAND消息。若是但願列表框對其中各項進行排序,那麼可使用另外一個風格LBS_SORT。
若是想創建一個多選選擇的列表框,那麼可使用風格LBS_MULTIPLESEL。
默認的列表框是無邊界的,因此通常都要加上WS_BORDER來加上邊界。
使用WS_VSCROLL來增長垂直滾動條。
有一個列表框風格,綜合了上述各類風格,那就是LBS_STANDARD風格。
97 將文本串放入列表框
將文本串放入列表框能夠經過調用SendMessage給列表框窗口過程發消息來實現這一點。文本串一般經過以0開始計數的下標數來引用,其中0對應於最頂上的項。
通常子窗口列表框控制的句柄定義爲hwndList
下標值定義爲iIndex
在使用SendMessage傳遞文本串的狀況下,lParam參數是指向null結尾串的指針。
當窗口過程存儲的列表框內容超過了可用內存空間時,SendMessage將返回LB_ERRSPACE(定義爲-2)。若是是其餘緣由出錯,那麼將返回LB_ERR(-1).
若是採用LBS_SORT風格,那麼填充列表框最簡單的方法是藉助LB_ADDSTRING消息:SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)szString);
//若是沒有采用LBS_SORT,那麼可使用LB_INSERTSTRING指定一個下標值,將字符串插入到列表框中: SendMessage (hwndList, LB_INSERTSTRING, iIndex, (LPARAM) szString) ; //若是iIndex等於4,那麼szString將變爲下標值爲4的串——從頂頭開始算起的第5個串。下標值爲-1時,將串增長在最後。 //能夠在指定下標值的同時使用LB_DELETESTRING參數,這就能夠從列表框中刪除串: SendMessage (hwndList, LB_DELETESTRING, iIndex, 0); //可使用LB_RESETCONTENT清除列表框中的全部內容: SendMessage (hwndList, LB_RESETCONTENT, 0, 0);
98 選擇和獲取項
//獲取列表框項數:(LB_GETCOUNT) iCount = SendMessage (hwndList, LB_GETCOUNT, 0, 0); //加亮選中項:(LB_SETCURSEL) SendMessage (hwndList, LB_SETCURSEL, iIndex, 0); //將lParam設置爲-1,取消全部選擇。 //根據項的第一個字母來選擇:(LB_SELSECTSTRING) iIndex = SendMessage (hwndList, LB_SELECTSTRING, iIndex, (LPARAM) szSearchString); //iIndex等於-1時,從頭開始搜索。 //當獲得來自列表框的WM_COMMAND消息時,能夠經過使用LB_GETCURSEL來肯定當前選項的下標: (LB_GETCURSEL) iIndex = SendMessage (hwndList, LB_GETCURSEL, 0, 0); //能夠肯定列表框中串的長度:(LB_GETTEXTLEN) iLength = SendMessage (hwndList, LB_GETTEXTLEN, iIndex, 0); //能夠將某項複製到文本緩衝區:(LB_GETTEXT) iLength = SendMessage (hwndList, LB_GETTEXT, iIndex, (LPARAM) szBuffer);
99 接收來自列表框的消息
當用戶用鼠標單擊列表框時,列表框將接收輸入焦點。
列表框控制向其父窗口發送WM_COMMAND消息,對按鈕和編輯控制來講wParam和lParam參數的含義是相同的。
LOWORD(wParam) 子窗口ID HIWORD(wParam) 通知碼(LBN開頭) lParam 子窗口句柄 //通知碼及其值以下所示: LBN_ERRSPACE -2 表示列表框已經超出運行空間 LBN_SELCHANGE 1 代表當前選擇已經被改變 LBN_DBLCLK 2 代表某項已經被鼠標雙擊 LBN_SELCANCEL 3 LBN_SETFOCUS 4 列表框得到焦點 LBN_KILLFOCUS 5 列表框失去焦點 //只有列表框窗口風格包括LBS_NOTIFY時,列表框控制才向父窗口發送LBN_SELCHANGE和LBN_DBLCLK碼
100 文件列表
LB_DIR是功能最強的列表框消息,它用文件目錄表填充列表框,而且能夠選擇將子目錄和有效的磁盤驅動器也包括進來:
SendMessage(hwndList, LB_DIR, iAttr, (LPARAM)szFileSpec);
① 使用文件屬性碼:
當LB_DIR消息的iAttr值爲DDL_READWRITE時,列表框列出普通文件、只讀文件和具備歸檔位集的文件。
當值爲DDL_DIRECTORY時,列表框除列出上述文件以外,還列出子目錄,目錄位於方括號以內。
當值爲DDL_DRIVES | DDL_DIRECTORY時,那麼列表將擴展到包括全部有效的驅動器,驅動器字母顯示在虛線之間。
當值爲DDL_EXCLUSIVE | DDL_ARCHIVE時,即將iAttr的最高位置位能夠列出帶標誌的文件,而不包括普通文件。
② 文件列表的排序
lParam參數是指向文件說明串如「*.*」的指針,這個文件說明串不影響列表框中的子目錄。
用戶也許但願給列有文件清單的列表框使用LBS_SORT消息,列表框首先列出符合文件說明的文件,再列出子目錄名。列出的第一個子目錄名將採用下面的格式:[..]這種兩個點的子目錄項容許用戶向根目錄返回一級。最後,具體的子目錄名採用下面的形式:[SUBDIR]後面是如下面的形式列出的有效磁盤驅動器[-A-]