版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、見(jiàn)招拆招Windows程序設(shè)計(jì)(三) 作者:Zoologist 于2007-11-16上傳 本期內(nèi)容非常多,我建議不要嘗試一口氣讀完。過(guò)去有一種職業(yè)叫做“采珠女”,她們的工作就是潛入海底,撈取珍珠(這個(gè)職業(yè)多是婦女,據(jù)說(shuō)婦女耐力好,能忍受低溫,可以潛得更深)。我們現(xiàn)在學(xué)習(xí)Win32匯編語(yǔ)言,和她們的工作差不多,每一次的學(xué)習(xí)過(guò)程好比下潛,每一次收獲的知識(shí)則是我們的“珍珠”。在閱讀本期的時(shí)候不妨在我們標(biāo)記“總結(jié)”的地方休息一下,好比浮上來(lái)?yè)Q氣,也順便盤(pán)點(diǎn)收獲。輸出文字 在上一期,您看到了一個(gè)簡(jiǎn)單的Windows程序,它在窗口中央,或者更準(zhǔn)確地說(shuō),在顯示區(qū)域中央顯示一行文字。正如我們學(xué)到的,顯示區(qū)域
2、是整個(gè)應(yīng)用程序窗口中未被標(biāo)題列、窗口邊框,以及可選的菜單列、工具列、狀態(tài)列和滾動(dòng)條占據(jù)的部分。簡(jiǎn)而言之,顯示區(qū)域是窗口中可以由程序任意書(shū)寫(xiě)和傳遞視覺(jué)信息的部分。對(duì)于程序的顯示區(qū)域,您幾乎可以為所欲為,只不過(guò)您不能假定窗口大小是某一特定尺寸,或者在程序執(zhí)行時(shí)其大小會(huì)保持不變。如果您不熟悉圖形窗口環(huán)境的程序設(shè)計(jì),這些限制可能會(huì)使您感到驚訝:不能再假設(shè)屏幕上的一行文字一定有80個(gè)字符了。您的程序必須與其它Windows程序共享顯示器。Windows使用者控制程序窗口在屏幕上顯示的方式。盡管可以建立固定大小的窗口(這對(duì)于計(jì)算器之類(lèi)的應(yīng)用是合理的),但在大多數(shù)情況下,使用者應(yīng)該能夠改變應(yīng)用程序窗口的大小
3、。您的程序必須能夠接受指定給它的大小,并且合理地利用這一空間。這有兩種可能的情況。一種可能是,程序只有僅能顯示hello的顯示區(qū)域;還有另一種可能,即程序在一個(gè)大屏幕、高分辨率的系統(tǒng)上執(zhí)行,其顯示區(qū)域大得足以顯示兩整頁(yè)文字。靈活地處理這兩種極端是Windows程序設(shè)計(jì)的要點(diǎn)之一。 這一章,我們將講述程序在顯示區(qū)域顯示信息的方式,但比上一期說(shuō)明的顯示方式更加復(fù)雜。當(dāng)程序在顯示區(qū)域顯示文字或圖形時(shí),它經(jīng)常要繪制它的顯示區(qū)域。這里著重講述繪制的方法。盡管Windows為顯示圖形提供了強(qiáng)大的圖形設(shè)備接口(GDI)函數(shù),但在這一章中,我只介紹簡(jiǎn)單文字行的顯示。我也將忽略Windows能夠使用的不同字體外
4、形及字體大小,僅使用Windows的內(nèi)定系統(tǒng)字體。這看起來(lái)似乎是一種限制,其實(shí)不然,本章涉及和解決的問(wèn)題適用于所有Windows程序設(shè)計(jì)。在混合顯示文字和圖形時(shí),Windows內(nèi)定字體的字符大小通常決定了圖形的尺寸。這期的內(nèi)容表面上是討論繪圖的方法,實(shí)際上是討論與設(shè)備無(wú)關(guān)的程序設(shè)計(jì)基礎(chǔ)。Windows程序只能對(duì)顯示區(qū)域大小甚至字符的大小做很少的假定,相反地,必須使用Windows提供的功能來(lái)取得關(guān)于程序執(zhí)行環(huán)境的信息,形象地說(shuō)就是“走一步看一步”“具體情況具體分析”。繪制和更新 在文字模式環(huán)境下,程序可以在顯示器的任意部分輸出,程序輸出到屏幕上的內(nèi)容會(huì)停留在原處,不會(huì)神秘地消失。因此,程序可以
5、丟掉重新生成屏幕顯示時(shí)所需的信息。在Windows中,只能在窗口的顯示區(qū)域繪制文字和圖形,而且不能確保在顯示區(qū)域內(nèi)顯示的內(nèi)容會(huì)一直保留到程序下一次有意地改寫(xiě)它時(shí)還保留在那里。例如,使用者可能會(huì)在屏幕上移動(dòng)另一個(gè)程序的窗口,這樣就可能覆蓋您的應(yīng)用程序窗口的一部分。Windows不會(huì)保存您的窗口中被其它程序覆蓋的區(qū)域,當(dāng)程序移開(kāi)后,Windows會(huì)要求您的程序更新顯示區(qū)域的這個(gè)部分。Windows是一個(gè)消息驅(qū)動(dòng)系統(tǒng)。它通過(guò)把消息投入應(yīng)用程序消息隊(duì)列中或者把消息發(fā)送給合適的窗口消息處理程序,將發(fā)生的各種事件通知給應(yīng)用程序。Windows通過(guò)發(fā)送WM_PAINT消息通知窗口消息處理程序,窗口的部分顯示
6、區(qū)域需要繪制。WM_PAINT消息 大多數(shù)Windows程序在WinMain中進(jìn)入消息循環(huán)之前的初始化期間都要呼叫函數(shù)UpdateWindow。Windows利用這個(gè)機(jī)會(huì)給窗口消息處理程序發(fā)送第一個(gè)WM_PAINT消息。這個(gè)消息通知窗口消息處理程序:必須繪制顯示區(qū)域。此后,窗口消息處理程序應(yīng)在任何時(shí)刻都準(zhǔn)備好處理其它WM_PAINT消息,必要的話,甚至重新繪制窗口的整個(gè)顯示區(qū)域。在發(fā)生下面幾種事件之一時(shí),窗口消息處理程序會(huì)接收到一個(gè)WM_PAINT消息: 在使用者移動(dòng)窗口或顯示窗口時(shí),窗口中先前被隱藏的區(qū)域重新可見(jiàn)。 使用者改變窗口的大?。ㄈ绻翱陬?lèi)別樣式有著CS_HREDRAW和CS_VRE
7、DRAW位旗標(biāo)的設(shè)定)。 程序使用ScrollWindow或ScrollDC函數(shù)滾動(dòng)顯示區(qū)域的一部分。 程序使用InvalidateRect或InvalidateRgn函數(shù)刻意產(chǎn)生WM_PAINT消息。 對(duì)于上面,我們可以做一下實(shí)驗(yàn)來(lái)驗(yàn)證: 打開(kāi)Spy+,選中一個(gè)窗口,這個(gè)例子中,我選的是MasmPlus EXE模板直接建立的Win32 EXE,就是一個(gè)簡(jiǎn)單的窗口。使用“窗口查找程序工具”選定Gui窗口, 再切換到消息卡片上,清除全部,然后選定 WM_Paint 消息。 點(diǎn)確定之后就開(kāi)始監(jiān)視了: 我們?cè)囍鴮ⅲ琒py+窗口移動(dòng)到Gui.exe上,并沒(méi)有消息 但只要挪開(kāi)一點(diǎn)點(diǎn),就會(huì)產(chǎn)生消息 除此之
8、外,還可以試試看:鼠標(biāo)移動(dòng)到Gui.EXE上是否會(huì)產(chǎn)生消息等等。 在某些情況下,顯示區(qū)域的一部分被臨時(shí)覆蓋,Windows試圖保存一個(gè)顯示區(qū)域,并在以后恢復(fù)它,但這不一定能成功。在以下情況下,Windows可能發(fā)送WM_PAINT消息: Windows擦除覆蓋了部分窗口的對(duì)話框或消息框。 菜單下拉出來(lái),然后被釋放。 顯示工具提示消息。在某些情況下,Windows總是保存它所覆蓋的顯示區(qū)域,然后恢復(fù)它。這些情況是:鼠標(biāo)光標(biāo)穿越顯示區(qū)域。 圖標(biāo)拖過(guò)顯示區(qū)域。 處理WM_PAINT消息要求程序?qū)懽髡吒淖冏约合蝻@示器輸出的思維方式。程序應(yīng)該組織成可以保留繪制顯示區(qū)域需要的所有信息,并且僅當(dāng)響應(yīng)要求即W
9、indows給窗口消息處理程序發(fā)送WM_PAINT消息時(shí)才進(jìn)行繪制。如果程序在其它時(shí)間需要更新其顯示區(qū)域,它可以強(qiáng)制Windows產(chǎn)生一個(gè)WM_PAINT消息。這看來(lái)似乎是在屏幕上顯示內(nèi)容的一種舍近求遠(yuǎn)的方法。但您的程序結(jié)構(gòu)可以從中受益。有效矩形和無(wú)效矩形 盡管窗口消息處理程序一旦接收到WM_PAINT消息之后,就準(zhǔn)備更新整個(gè)顯示區(qū)域,但它經(jīng)常只需要更新一個(gè)較小的區(qū)域(最常見(jiàn)的是顯示區(qū)域中的矩形區(qū)域)。顯然,當(dāng)對(duì)話框覆蓋了部分顯示區(qū)域時(shí),情況即是如此。在擦除對(duì)話框之后,需要重畫(huà)的只是先前被對(duì)話框遮住的矩形區(qū)域。(按照上面的方法,你可以試驗(yàn)一下,當(dāng)GUI.EXE被一個(gè)窗口完全覆蓋,它是不會(huì)收到消
10、息的)這個(gè)區(qū)域稱為無(wú)效區(qū)域或更新區(qū)域。正是顯示區(qū)域內(nèi)無(wú)效區(qū)域的存在,才會(huì)讓W(xué)indows將一個(gè)WM_PAINT消息放在應(yīng)用程序的消息隊(duì)列中。只有在顯示區(qū)域的某一部分失效時(shí),窗口才會(huì)接受WM_PAINT消息。 Windows內(nèi)部為每個(gè)窗口保存一個(gè)繪圖信息結(jié)構(gòu),這個(gè)結(jié)構(gòu)包含了包圍無(wú)效區(qū)域的最小矩形的坐標(biāo)以及其它信息,這個(gè)矩形就叫做無(wú)效矩形,有時(shí)也稱為無(wú)效區(qū)域。如果在窗口消息處理程序處理WM_PAINT消息之前顯示區(qū)域中的另一個(gè)區(qū)域變?yōu)闊o(wú)效,則Windows計(jì)算出一個(gè)包圍兩個(gè)區(qū)域的新的無(wú)效區(qū)域(以及一個(gè)新的無(wú)效矩形),并將這種變化后的信息放在繪制信息結(jié)構(gòu)中。Windows不會(huì)將多個(gè)WM_PAINT消
11、息都放在消息隊(duì)列中。 窗口消息處理程序可以通過(guò)呼叫InvalidateRect使顯示區(qū)域內(nèi)的矩形無(wú)效。如果消息隊(duì)列中已經(jīng)包含一個(gè)WM_PAINT消息,Windows將計(jì)算出新的無(wú)效矩形。否則,它將一個(gè)新的WM_PAINT消息放入消息隊(duì)列中。在接收到WM_PAINT消息時(shí),窗口消息處理程序可以取得無(wú)效矩形的坐標(biāo)(我們馬上就會(huì)看到這一點(diǎn))。通過(guò)呼叫GetUpdateRect,可以在任何時(shí)候取得這些坐標(biāo)。在處理WM_PAINT消息處理期間,窗口消息處理程序在呼叫了BeginPaint之后,整個(gè)顯示區(qū)域即變?yōu)橛行?。程序也可以通過(guò)呼叫ValidateRect函數(shù)使顯示區(qū)域內(nèi)的任意矩形區(qū)域變?yōu)橛行АH绻@
12、呼叫具有令整個(gè)無(wú)效區(qū)域變?yōu)橛行У男Ч?,則目前隊(duì)列中的任何WM_PAINT消息都將被刪除。 總結(jié):上面一段敘述讓人感覺(jué)非??菰铮啾戎挛疫€是喜歡用代碼說(shuō)話。如果這一段看不明白不要緊,下面還會(huì)再提到。GDI 簡(jiǎn)介 要在窗口的顯示區(qū)域繪圖,可以使用Windows的圖形設(shè)備接口(GDI)函數(shù)。Windows提供了幾個(gè)GDI函數(shù),用于將字符串輸出到窗口的顯示區(qū)域內(nèi)。我們已經(jīng)在上一期看過(guò)DrawText函數(shù),但是目前使用最為普遍的文字輸出函數(shù)是TextOut。該函數(shù)的格式如下:TextOut PROTO hdc:HDC,nXStart:DWORD,nYStart:DWORD,lpString:DWORD
13、,cbString:DWORDHDC :Dc的handlenXStart :開(kāi)始位置的X坐標(biāo)nYStart :開(kāi)始位置的Y坐標(biāo)lpString :要顯示的字符串cbString :字符數(shù)量 TextOut向窗口的顯示區(qū)域?qū)懭胱址?。lpString參數(shù)是指向字符串的指針,cbString 是字符串的長(zhǎng)度。nXStart和nYStart參數(shù)定義了字符串在顯示區(qū)域的開(kāi)始位置。hdc參數(shù)是設(shè)備內(nèi)容句柄,它是GDI的重要部分。實(shí)際上,每個(gè)GDI函數(shù)都需要將這個(gè)句柄作為函數(shù)的第一個(gè)參數(shù)。設(shè)備內(nèi)容 讀者可能還記得,句柄只不過(guò)是一個(gè)數(shù)值,Windows以它在內(nèi)部使用對(duì)象。程序?qū)懽髡邚腤indows取得句柄,
14、然后在其它函數(shù)中使用該句柄。設(shè)備內(nèi)容句柄是GDI函數(shù)的窗口通行證,有了這種設(shè)備內(nèi)容句柄,程序?qū)懽髡呔湍茏匀绲卦陲@示區(qū)域上繪圖,使圖形如自己所愿地變得好看或者難看。設(shè)備內(nèi)容(簡(jiǎn)稱為DC)實(shí)際上是GDI內(nèi)部保存的數(shù)據(jù)結(jié)構(gòu)。設(shè)備內(nèi)容與特定的顯示設(shè)備(如顯示器或打印機(jī))相關(guān)。對(duì)于顯示器,設(shè)備內(nèi)容總是與顯示器上的特定窗口相關(guān)。設(shè)備內(nèi)容中的有些值是圖形屬性,這些屬性定義了GDI繪圖函數(shù)工作的細(xì)節(jié)。例如,對(duì)于TextOut,設(shè)備內(nèi)容的屬性確定了文字的顏色、文字的背景色、x坐標(biāo)和y坐標(biāo)映像到窗口的顯示區(qū)域的方式,以及顯示文字時(shí)Windows使用的字體。 當(dāng)程序需要繪圖時(shí),它必須先取得設(shè)備內(nèi)容句柄。在取得了該句
15、柄后,Windows用內(nèi)定的屬性值填入內(nèi)部設(shè)備內(nèi)容結(jié)構(gòu)。在后面您會(huì)看到,可以通過(guò)呼叫不同的GDI函數(shù)改變這些默認(rèn)值。利用其它的GDI函數(shù)可以取得這些屬性的目前值。當(dāng)然,還有其它的GDI函數(shù)能夠在窗口的顯示區(qū)域真正地繪圖。當(dāng)程序在顯示區(qū)域繪圖完畢后,它必須釋放設(shè)備內(nèi)容句柄。句柄被程序釋放后就不再有效,且不能再被使用。程序必須在處理單個(gè)消息處理期間取得和釋放句柄。除了呼叫CreateDC建立的設(shè)備內(nèi)容之外,程序不能在兩個(gè)消息之間保存其它設(shè)備內(nèi)容句柄。 Windows應(yīng)用程序一般使用兩種方法來(lái)取得設(shè)備內(nèi)容句柄,以備在屏幕上繪圖。取得設(shè)備內(nèi)容句柄:方法一 在處理WM_PAINT消息時(shí),使用這種方法。它
16、涉及BeginPaint和EndPaint兩個(gè)函數(shù),這兩個(gè)函數(shù)需要窗口句柄(作為參數(shù)傳給窗口消息處理程序)和PAINTSTRUCT結(jié)構(gòu)的變量(在Windows.inc中定義)的地址為參數(shù)。Windows程序?qū)懽髡咄ǔ0堰@一結(jié)構(gòu)變量命名為ps并且在窗口消息處理程序中定義它: PAINTSTRUCT ps ; 在處理WM_PAINT消息時(shí),窗口消息處理程序首先呼叫BeginPaint。BeginPaint函數(shù)一般在準(zhǔn)備繪制時(shí)導(dǎo)致無(wú)效區(qū)域的背景被擦除。該函數(shù)也填入ps結(jié)構(gòu)的字段。BeginPaint傳回的值是設(shè)備內(nèi)容句柄,這一傳回值通常被保存在叫做hdc的變量中。它在窗口消息處理程序中的定義如下:
17、HDC hdc ; HDC數(shù)據(jù)型態(tài)定義為32位的無(wú)正負(fù)號(hào)整數(shù)。然后,程序就可以使用需要設(shè)備內(nèi)容句柄的TextOut等GDI函數(shù)。呼叫EndPaint即可釋放設(shè)備內(nèi)容句柄。 一般地,處理WM_PAINT消息的形式如下:.if eax = WM_PAINT ;eax為 uMsg Invoke BeginPaint,hWnd,addr stPS ; 使用GDI函數(shù) Invoke EndPaint,hWnd,addr stPS Xor eax,eax ret .endif 在處理WM_PAINT消息時(shí),必須成對(duì)地呼叫BeginPaint和EndPaint。如果窗口消息處理程序不處理WM_PAINT消息
18、,則它必須將WM_PAINT消息傳遞給Windows中DefWindowProc(內(nèi)定窗口消息處理程序)。DefWindowProc以下列代碼處理WM_PAINT消息:.if eax = WM_PAINT ;eax為 uMsg Invoke BeginPaint,hWnd,addr stPS Invoke EndPaint,hWnd,addr stPS Xor eax,eax ret .endif 這兩個(gè)BeginPaint和EndPaint呼叫之間中沒(méi)有任何敘述,僅僅使先前無(wú)效區(qū)域變?yōu)橛行?。但以下方法是錯(cuò)誤的:case WM_PAINT: return 0 ; / WRONG !.if ea
19、x = WM_PAINT ;eax為 uMsg Ret.endif Windows將一個(gè)WM_PAINT消息放到消息隊(duì)列中,是因?yàn)轱@示區(qū)域的一部分無(wú)效。如果不呼叫BeginPaint和EndPaint(或者ValidateRect),則Windows不會(huì)使該區(qū)域變?yōu)橛行?。相反,Windows將發(fā)送另一個(gè)WM_PAINT消息,且一直發(fā)送下去。使用Spy+可以看到,潮水一般的 WM_PAINT 不斷發(fā)出來(lái),并且CPU占用率也一直很高(我是雙核CPU,一個(gè)CPU占滿了,因此右下角的CPU使用率一直在50%左右,如果是單核CPU就是100%了)。 繪圖信息結(jié)構(gòu) 前面提到過(guò),Windows為每個(gè)窗口保存
20、一個(gè)繪圖信息結(jié)構(gòu),這就是PAINTSTRUCT,定義如下:PAINTSTRUCT STRUCT hdc DWORD ? fErase DWORD ? rcPaint RECT fRestore DWORD ? fIncUpdate DWORD ? rgbReserved BYTE 32 dup(?)PAINTSTRUCT ENDS 在程序呼叫BeginPaint時(shí),Windows會(huì)適當(dāng)填入該結(jié)構(gòu)的各個(gè)字段值。使用者程序只使用前三個(gè)字段,其它字段由Windows內(nèi)部使用。hdc字段是設(shè)備內(nèi)容句柄。在舊版本的Windows中,BeginPaint的傳回值也曾是這個(gè)設(shè)備內(nèi)容句柄。在大多數(shù)情況下, f
21、Erase被標(biāo)志為FALSE(0),這意味著Windows已經(jīng)擦除了無(wú)效矩形的背景。這最早在BeginPaint函數(shù)中發(fā)生(如果要在窗口消息處理程序中自己定義一些背景擦除行為,可以自行處理WM_ERASEBKGND消息)。 比如,在MasmPlus GUI模板程序中處理WM_ERASEBKGND消息,如下: .elseif uMsg = WM_PAINT invoke BeginPaint,hWin,addr ps invoke EndPaint, hWin,addr ps .elseif uMsg = WM_ERASEBKGND mov eax,0 .elseif uMsg = WM_DES
22、TROY invoke PostQuitMessage,NULL 就會(huì)使得這個(gè)窗口的行為看起來(lái)非常怪異。當(dāng)系統(tǒng)特別煩忙的時(shí)候,我們的窗口就會(huì)出現(xiàn)類(lèi)似的“病癥”。下圖是用記事本程序劃過(guò)這個(gè)窗口之后的景象。 Windows使用WNDCLASS結(jié)構(gòu)的hbrBackground字段指定的畫(huà)刷來(lái)擦除背景,這個(gè)WNDCLASS結(jié)構(gòu)是程序在WinMain初始化期間登錄窗口類(lèi)別時(shí)使用的。許多Windows程序使用白色畫(huà)刷。以下敘述設(shè)定窗口類(lèi)別結(jié)構(gòu)字段值:invoke GetStockObject,WHITE_BRUSHmov wndclass.hbrBackground,EAX 不過(guò),如果程序通過(guò)呼叫Wind
23、ows函數(shù)InvalidateRect使顯示區(qū)域中的矩形失效,則該函數(shù)的最后一個(gè)參數(shù)會(huì)指定是否擦除背景。如果這個(gè)參數(shù)為FALSE(即0),則Windows將不會(huì)擦除背景,并且在呼叫完BeginPaint后PAINTSTRUCT結(jié)構(gòu)的fErase字段將為T(mén)RUE(非零)。 PAINTSTRUCT結(jié)構(gòu)的rcPaint字段是RECT型態(tài)的結(jié)構(gòu)。您已經(jīng)在前面中看到,RECT結(jié)構(gòu)定義了一個(gè)矩形,其四個(gè)字段為left、top、right和bottom。PAINTSTRUCT結(jié)構(gòu)的rcPaint字段定義了無(wú)效矩形的邊界,如圖4-1所示。這些值均以圖素為單位,并相對(duì)于顯示區(qū)域的左上角。無(wú)效矩形是應(yīng)該重畫(huà)的區(qū)域
24、。 圖4-1 無(wú)效矩形的邊界 PAINTSTRUCT中的rcPaint矩形不僅是無(wú)效矩形,它還是一個(gè)剪取矩形。這意味著Windows將繪圖操作限制在剪取矩形內(nèi)(更確切地說(shuō),如果無(wú)效矩形區(qū)域不為矩形,則Windows將繪圖操作限制在這個(gè)區(qū)域內(nèi))。 在處理WM_PAINT消息時(shí),為了在更新的矩形外繪圖,可以使用如下呼叫: invoke InvalidateRect, hWnd,NULL,FALSE 該呼叫在BeginPaint呼叫之前進(jìn)行,它使整個(gè)顯示區(qū)域變?yōu)闊o(wú)效,并擦除背景。但是,如果最后一個(gè)參數(shù)等于FALSE,則不擦除背景,原有的東西將保留在原處。 通常這是Windows程序在無(wú)論何時(shí)收到WM
25、_PAINT消息而不考慮rcPaint結(jié)構(gòu)的情況下簡(jiǎn)單地重畫(huà)整個(gè)顯示區(qū)域最方便的方法。例如,如果在顯示區(qū)域的顯示輸出中包括了一個(gè)圓,但是只有圓的一部分落到了無(wú)效矩形中,它就使僅繪制圓的無(wú)效部分變得沒(méi)有意義。這需要畫(huà)整個(gè)圓。在您使用從BeginPaint傳回的設(shè)備內(nèi)容句柄時(shí),Windows不會(huì)繪制rcPaint矩形外的任何部分。 在前面的HelloWin中,我們并不關(guān)心處理WM_PAINT消息時(shí)的無(wú)效矩形。如果文字顯示區(qū)域恰巧在無(wú)效矩形內(nèi),則由DrawText恢復(fù)之。否則,在處理DrawText呼叫的某個(gè)時(shí)刻,Windows會(huì)確定它無(wú)須向顯示器上輸出。不過(guò),這一決定需要時(shí)間。關(guān)心程序性能和速度的
26、程序?qū)懽髡呦M谔幚鞼M_PAINT期間使用無(wú)效矩形范圍,以避免不必要的GDI呼叫。如果繪制時(shí)需要存取例如位圖這樣的磁盤(pán)文件,則這就顯得尤其重要。取得設(shè)備內(nèi)容句柄:方法二 雖然最好是在處理WM_PAINT消息處理期間更新整個(gè)顯示區(qū)域,但是您也會(huì)發(fā)現(xiàn)在處理非WM_PAINT消息處理期間繪制顯示區(qū)域的某個(gè)部分也是非常有用的?;蛘吣枰獙⒃O(shè)備內(nèi)容句柄用于其它目的,如取得設(shè)備內(nèi)容的信息。 要得到窗口顯示區(qū)域的設(shè)備內(nèi)容句柄,可以呼叫GetDC來(lái)取得句柄,在使用完后呼叫ReleaseDC:invoke GetDC,hwndmov hdc,eax使用GDI函數(shù)ReleaseDC (hwnd, hdc) In
27、voke ReleaseDC,hwn,hdc 與BeginPaint和EndPaint一樣,GetDC和ReleaseDC函數(shù)必須成對(duì)地使用。如果在處理某消息時(shí)呼叫GetDC,則必須在退出窗口消息處理程序之前呼叫ReleaseDC。不要在一個(gè)消息中呼叫GetDC卻在另一個(gè)消息呼叫ReleaseDC。 與從BeginPaint傳回設(shè)備內(nèi)容句柄不同,GetDC傳回的設(shè)備內(nèi)容句柄具有一個(gè)剪取矩形,它等于整個(gè)顯示區(qū)域??梢栽陲@示區(qū)域的某一部分繪圖,而不只是在無(wú)效矩形上繪圖(如果確實(shí)存在無(wú)效矩形)。與BeginPaint不同,GetDC不會(huì)使任何無(wú)效區(qū)域變?yōu)橛行АH绻枰拐麄€(gè)顯示區(qū)域有效,可以呼叫In
28、voke ValidateRect,hwnd,NULL 一般可以呼叫GetDC和ReleaseDC來(lái)對(duì)鍵盤(pán)消息(如在字處理程序中)和鼠標(biāo)消息(如在畫(huà)圖程序中)作出反應(yīng)。此時(shí),程序可以立刻根據(jù)使用者的鍵盤(pán)或鼠標(biāo)輸入來(lái)更新顯示區(qū)域,而不需要考慮為了窗口的無(wú)效區(qū)域而使用WM_PAINT消息。不過(guò),一旦確實(shí)收到了WM_PAINT消息,程序就必須要收集足夠的信息后才能更新顯示。 與GetDC相似的函數(shù)是GetWindowDC。GetDC傳回用于寫(xiě)入窗口顯示區(qū)域的設(shè)備內(nèi)容句柄,而GetWindowDC傳回寫(xiě)入整個(gè)窗口的設(shè)備內(nèi)容句柄。例如,您的程序可以使用從GetWindowDC傳回的設(shè)備內(nèi)容句柄在窗口的標(biāo)
29、題列上寫(xiě)入文字。然而,程序同樣也應(yīng)該處理WM_NCPAINT (非顯示區(qū)域繪制)消息。TextOut:細(xì)節(jié)TextOut是用于顯示文字的最常用的GDI函數(shù)。語(yǔ)法是:TextOut (hdc, x, y, psText, iLength) ; 以下將詳細(xì)地討論這個(gè)函數(shù)。 第一個(gè)參數(shù)是設(shè)備內(nèi)容句柄,它既可以是GetDC的傳回值,也可以是在處理WM_PAINT消息時(shí)BeginPaint的傳回值。 設(shè)備內(nèi)容的屬性控制了被顯示的字符串的特征。例如,設(shè)備內(nèi)容中有一個(gè)屬性指定文字顏色,內(nèi)定顏色為黑色;內(nèi)定設(shè)備內(nèi)容還定義了白色的背景。在程序向顯示器輸出文字時(shí),Windows使用這個(gè)背景色來(lái)填入字符周?chē)木匦慰?/p>
30、間(稱為字符框)。 該文字背景色與定義窗口類(lèi)別時(shí)設(shè)置的背景并不相同。窗口類(lèi)別中的背景是一個(gè)畫(huà)刷,它是一種純色或者非純色組成的畫(huà)刷,Windows用它來(lái)擦除顯示區(qū)域,它不是設(shè)備內(nèi)容結(jié)構(gòu)的一部分。在定義窗口類(lèi)別結(jié)構(gòu)時(shí),大多數(shù)Windows應(yīng)用程序使用WHITE_BRUSH,以便內(nèi)定設(shè)備內(nèi)容中的內(nèi)定文字背景顏色與Windows用以擦除顯示區(qū)域背景的畫(huà)刷顏色相同。 psText參數(shù)是指向字符串的指針,iLength是字符串中字符的個(gè)數(shù)。如果psText指向Unicode字符串,則字符串中的字節(jié)數(shù)就是iLength值的兩倍。字符串中不能包含任何ASCII控制字符(如回車(chē)、換行、制表或退格),Window
31、s會(huì)將這些控制字符顯示為實(shí)心塊。Text0ut不識(shí)別作為字符串結(jié)束標(biāo)志的內(nèi)容為零的字節(jié)(對(duì)于Unicode,是一個(gè)短整數(shù)型態(tài)的0),而需要由nLength參數(shù)指明長(zhǎng)度。TextOut中的x和y定義顯示區(qū)域內(nèi)字符串的開(kāi)始位置,x是水平位置,y是垂直位置。字符串中第一個(gè)字符的左上角位于坐標(biāo)點(diǎn)(x,y)。在內(nèi)定的設(shè)備內(nèi)容中,原點(diǎn)(x和y均為0的點(diǎn))是顯示區(qū)域的左上角。如果在TextOut中將x和y設(shè)為0,則將從顯示區(qū)域左上角開(kāi)始輸出字符串。 當(dāng)您閱讀GDI繪圖函數(shù)(例如TextOut)的文件時(shí),就會(huì)發(fā)現(xiàn)傳遞給函數(shù)的坐標(biāo)常常被稱為邏輯坐標(biāo)。在后面我們會(huì)詳細(xì)地解釋這種情況?,F(xiàn)在請(qǐng)注意,Windows有許
32、多坐標(biāo)映像方式,它們用來(lái)控制GDI函數(shù)指定的邏輯坐標(biāo)轉(zhuǎn)換為顯示器的實(shí)際圖素坐標(biāo)的方式。映像方式在設(shè)備內(nèi)容中定義,內(nèi)定映像方式是MM_TEXT(使用WINGDI.H中定義的標(biāo)識(shí)符)。在MM_TEXT映像方式下,邏輯單位與實(shí)際單位相同,都是像素;x的值從左向右遞增,y的值從上向下遞增(參看圖4-2)。MM_TEXT坐標(biāo)系與Windows在PAINTSTRUCT結(jié)構(gòu)中定義無(wú)效矩形時(shí)使用的坐標(biāo)系相同,這為我們帶來(lái)了很多方便(但是,其它映像方式并非如此)。 圖4-2 MM_TEXT映像方式下的x坐標(biāo)和y坐標(biāo) 設(shè)備內(nèi)容也定義了一個(gè)剪裁區(qū)域。您已經(jīng)看到,對(duì)于從GetDC取得的設(shè)備內(nèi)容句柄,內(nèi)定剪裁區(qū)域是整個(gè)
33、顯示區(qū)域;而對(duì)于從BeginPaint取得的設(shè)備內(nèi)容句柄,則為無(wú)效區(qū)域。Windows不會(huì)在剪裁區(qū)域之外的任何位置顯示字符串。如果一個(gè)字符有一部分在剪裁區(qū)域外,則Windows將只顯示此區(qū)域內(nèi)的那部分。要想將輸出寫(xiě)到窗口的顯示區(qū)域之外不是那么容易的,所以不用擔(dān)心會(huì)無(wú)意間出現(xiàn)這種事情。系統(tǒng)字體 設(shè)備內(nèi)容還定義了在您呼叫TextOut顯示文字時(shí)Windows使用的字體。內(nèi)定字體為系統(tǒng)字體,或用Windows表頭文件中的標(biāo)識(shí)符,即SYSTEM_FONT。系統(tǒng)字體是Windows用來(lái)在標(biāo)題列、菜單和對(duì)話框中顯示字符串的內(nèi)定字體。 在Windows的早期版本中,系統(tǒng)字體是等寬(fixed-pitch)字
34、體,這意味著所有字符均具有同樣的寬度,非常類(lèi)似于打字機(jī)。然而,從Windows 3.0開(kāi)始,系統(tǒng)字體成為一種變寬(variable-pitch)字體,這意味著不同的字符具有不同的大小,比如,W要比i寬。變寬字體比等寬字體好讀,這已經(jīng)是公認(rèn)的事實(shí)。不過(guò),可以想見(jiàn),這一轉(zhuǎn)變使很多原來(lái)的Windows程序代碼不再適用,從而要求程序?qū)懽髡邔W(xué)習(xí)一些使用字體的新技術(shù)。(題外話:英文報(bào)紙的編輯比中文報(bào)紙編輯的痛苦得多,他們每次都要精確的調(diào)整標(biāo)題長(zhǎng)度,而中文都是方塊字很容易計(jì)算標(biāo)題的長(zhǎng)度) 系統(tǒng)字體是一種點(diǎn)陣字體,這意味著字符被定義為像素塊(在后面,將討論TrueType字體,它是由輪廓定義的)。至于確切的大
35、小,系統(tǒng)字體的字符大小取決于顯示器的大小。系統(tǒng)字體設(shè)計(jì)為至少能在顯示器上顯示25行80列文字。字符大小TEXTMETRICA STRUCT tmHeight DWORD ? tmAscent DWORD ? tmDescent DWORD ? tmInternalLeading DWORD ? tmExternalLeading DWORD ? tmAveCharWidth DWORD ? tmMaxCharWidth DWORD ? tmWeight DWORD ? tmOverhang DWORD ? tmDigitizedAspectX DWORD ? tmDigitizedAspect
36、Y DWORD ? tmFirstChar BYTE ? tmLastChar BYTE ? tmDefaultChar BYTE ? tmBreakChar BYTE ? tmItalic BYTE ? tmUnderlined BYTE ? tmStruckOut BYTE ? tmPitchAndFamily BYTE ? tmCharSet BYTE ?TEXTMETRICA ENDSTEXTMETRIC equ 這些字段值的單位取決于選定的設(shè)備內(nèi)容映像方式。在內(nèi)定設(shè)備內(nèi)容下,映像方式是MM_TEXT,因此值的大小是以圖素為單位。要使用GetTextMetrics函數(shù),需要先定義一個(gè)結(jié)
37、構(gòu)變量(通常稱為tm):TEXTMETRIC tm ;在需要確定文字大小時(shí),先取得設(shè)備內(nèi)容句柄,再呼叫GetTextMetrics:invoke GetDC,hwnd invoke hdc,eaxInvoke GetTextMetrics,hdc,addr tm Invoke ReleaseDC,hwnd,hdc 此后,您就可以查看文字尺寸結(jié)構(gòu)中的值,并有可能保存其中的一些以備將來(lái)使用。文字大小:細(xì)節(jié) TEXTMETRIC結(jié)構(gòu)提供了關(guān)于目前設(shè)備內(nèi)容中選用的字體的豐富信息。但是,字體的縱向大小只由5個(gè)值確定,其中4個(gè)值如圖4-3所示。 圖4-3 定義字體中縱向字符大小的4個(gè)值 最重要的值是tmH
38、eight,它是tmAscent和tmDescent的和。這兩個(gè)值表示了基準(zhǔn)在線下字符的最大縱向高度。間距(leading)指打印機(jī)在兩行文字間插入的空間。在TEXTMETRIC結(jié)構(gòu)中,內(nèi)部的間距包括在tmAscent中(因此也在tmHeight中),并且它經(jīng)常是重音符號(hào)出現(xiàn)的地方。tmInternalLeading字段可被設(shè)成0,在這種情況下,加重音的字母會(huì)稍稍縮短以便容納重音符號(hào)。 TEXTMETRIC結(jié)構(gòu)還包括一個(gè)不包含在tmHeight值中的字段tmExternalLeading。它是字體設(shè)計(jì)者建議加在橫向字符之間的空間大小。在安排文字行之間的空隙時(shí),您可以接受設(shè)計(jì)者建議的值,也可以拒
39、絕它。在系統(tǒng)字體中tmExternalLeading可以為0,因此我沒(méi)有在圖4-3中顯示它。(盡管我不想告訴你們,圖4-3確實(shí)就是Windows在640480的顯示分辨率中使用的系統(tǒng)字體。) TEXTMETRICS結(jié)構(gòu)包含有描述字符寬度的兩個(gè)字段,即tmAveCharWidth(小寫(xiě)字母加權(quán)平均寬度)和tmMaxCharWidth(字體中最寬字符的寬度)。對(duì)于定寬字體,這兩個(gè)值是相等的(圖4-3中這些值分別為7和14)。 本章的范例程序還需要另一種字符寬度,即大寫(xiě)字母的平均寬度,這可以用tmAveCharWidth乘以150大致計(jì)算出來(lái)。 必須認(rèn)識(shí)到,系統(tǒng)字體的大小取決于Windows所執(zhí)行的
40、視訊顯示器的分辨率,在某些情況下,取決于使用者選取的系統(tǒng)字體的大小。Windows提供了一個(gè)與設(shè)備無(wú)關(guān)的圖形接口,但程序?qū)懽髡哌€是有事情要處理的。不要想當(dāng)然耳地猜測(cè)字體大小來(lái)寫(xiě)作Windows程序,也不要把值定死,您可以使用GetTextMetrics函數(shù)取得這一信息。 總結(jié):這個(gè)地方不妨結(jié)合本期的MasmPlus一文仔細(xì)研究看看。盡量多做一些試驗(yàn)來(lái)驗(yàn)證各種想法。格式化文字 Windows啟動(dòng)后,系統(tǒng)字體的大小就不會(huì)發(fā)生改變,所以在程序執(zhí)行過(guò)程中,程序?qū)懽髡咧恍枰艚幸淮蜧etTexMetrics。最好是在窗口消息處理程序中處理WM_CREATE消息時(shí)進(jìn)行此呼叫,WM_CREATE消息是窗口消
41、息處理程序接收的第一個(gè)消息。在WinMain中呼叫CreateWindow時(shí),Windows會(huì)以一個(gè)WM_CREATE消息呼叫窗口消息處理程序。假設(shè)要編寫(xiě)一個(gè)Windows程序,在顯示區(qū)域顯示幾行文字,這需要先取得字符寬度和高度。您可以在窗口消息處理程序內(nèi)定義兩個(gè)變量來(lái)保存平均字符寬度(cxChar)和總的字符高度(cyChar): cxChar DWORD ? cyChar DWORD ? 變量名的前綴c代表count,在這里指圖素?cái)?shù),與x和y結(jié)合,分別指寬和高。(原文后面還有“這些變量定義為static靜態(tài)變量,因?yàn)樗鼈冊(cè)诖翱谙⑻幚沓绦蛑刑幚砥渌ⅲㄈ鏦M_PAINT)時(shí)也應(yīng)該是有效的
42、。如果變量在函數(shù)外面定義,則不需要定義為static?!?C語(yǔ)言中“static靜態(tài)變量”只的是一種定義在函數(shù)中的變量。它有一個(gè)特點(diǎn):當(dāng)你在函數(shù)中修改之后,下次再調(diào)用這個(gè)函數(shù)這個(gè)變量的值仍然是你修改之后的值,不會(huì)因?yàn)槟阍俅芜M(jìn)入而被重新初始化,故而有“靜態(tài)”之稱。 細(xì)心的讀者可以發(fā)現(xiàn),cxChar, cxCaps, cyChar 這幾個(gè)變量我特意提取成為全局變量,就是上述原因。) 下面是取得系統(tǒng)字體的字符寬度和高度的WM_CREATE程序代碼: .if message=WM_CREATE invoke GetDC,hwnd mov hdc,eax invoke GetTextMetrics,hd
43、c,addr tm mov eax,tm.tmAveCharWidth mov cxChar,eax ;cxChar = tm.tmAveCharWidth mov eax,2 ;cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 test DWORD ptr tm.tmPitchAndFamily,1 jz f inc eax : push eax mov eax,cxChar pop ebx mul ebx shr eax,1 mov cxCaps,eax mov eax,tm.tmHeight ;cyChar = tm.tmHe
44、ight + tm.tmExternalLeading add eax,DWORD ptr tm.tmExternalLeading mov cyChar,eax invoke ReleaseDC,hwnd, hdcret 注意我在計(jì)算cyChar時(shí)包括了tmExternalLeading字段,雖然該字段在系統(tǒng)字體中為0,但是因?yàn)樗沟梦淖值目勺x性更好,所以還是應(yīng)該把它包括進(jìn)去。沿著窗口向下每隔cyChar圖素就會(huì)顯示一行文字。 您會(huì)發(fā)現(xiàn)常常需要顯示格式化的數(shù)字跟簡(jiǎn)單的字符串。我們這里使用的是wsprintf函數(shù),這個(gè)函數(shù)與printf相似,只是把格式化字符串放到字符串中。然后,可以用Text
45、Out將字符串輸出到顯示器上。非常方便的是,從sprintf和wsprintf傳回的值就是字符串的長(zhǎng)度。您可以將這個(gè)值傳遞給TextOut作為iLength參數(shù)。 綜合使用 現(xiàn)在,我們似乎已經(jīng)具備了在屏幕上顯示多行文字所需要的所有知識(shí)。我們知道如何在WM_PAINT消息處理期間取得一個(gè)設(shè)備內(nèi)容句柄,如何使用TextOut函數(shù)以及如何根據(jù)字符大小來(lái)安排字距,剩下的就是顯示一點(diǎn)有意義的東西了。 前面,我們大概知道從Windows的GetSystemMetrics函數(shù)中取得的信息是很有意義的,該函數(shù)傳回Windows中不同視覺(jué)組件的大小信息,如圖標(biāo)、光標(biāo)、標(biāo)題列和滾動(dòng)條等。它們的大小因顯示卡和驅(qū)動(dòng)程
46、序的不同而有所不同。GetSystemMetrics是在程序中完成與設(shè)備無(wú)關(guān)圖形輸出的重要函數(shù)。 該函數(shù)需要一個(gè)參數(shù),叫做索引,在Windows表頭文件定義了75個(gè)整數(shù)索引標(biāo)識(shí)符(標(biāo)識(shí)符的數(shù)量隨著每個(gè)版本的Windows的發(fā)布而不斷地增加,在Windows 1.0的程序?qū)懽髡呶募袃H列出了26個(gè))。GetSystemMetrics傳回一個(gè)整數(shù),這個(gè)整數(shù)通常就是參數(shù)中指定的圖形組件大小。 讓我們來(lái)編寫(xiě)一個(gè)程序,顯示一些可以從GetSystemMetrics呼叫中取得的信息,顯示格式為每種視覺(jué)組件一行。一般來(lái)說(shuō),這個(gè)地方最好另外定義為一個(gè)文件,使用 include包到主文件中,不過(guò) 下面這個(gè)程序中
47、,我為了看起來(lái)容易一些,特意定義在一起。 顯示信息的程序命名為SYSMETS1。SYSMETS1.ASM的源程序如程序4-2所示?,F(xiàn)在大多數(shù)程序代碼看起來(lái)都很熟悉。WinMain中的程序代碼實(shí)際上與HELLOWIN中的程序代碼相同,并且WndProc中的大部分程序代碼都已經(jīng)討論過(guò)了。程序4-2 ;MASMPlus 代碼模板 - 普通的 Windows 程序代碼.386.Model Flat, StdCallOption Casemap :NoneInclude windows.incInclude user32.incInclude kernel32.incInclude gdi32.incI
48、nclude winmm.incincludelib gdi32.libIncludeLib user32.libIncludeLib kernel32.libIncludeLib winmm.libinclude macro.asm WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD NUMLINES equ (sysmetricsEnd - sysmetrics) / 4 /3.DATA szAppName db SysMets1,0 szShow01 db SM_CXSCR
49、EEN,0 szShow02 db Screen width in pixels,0 szShow03 db SM_CYSCREEN,0 szShow04 db Screen height in pixels,0 szShow05 db SM_CXVSCROLL,0 szShow06 db Vertical scroll width,0 szShow07 db SM_CYHSCROLL,0 szShow08 db Horizontal scroll height,0 szShow09 db SM_CYCAPTION,0 szShow10 db Caption bar height,0 szShow11 db SM_CXBORDER,0 szShow12 db Window border width,0 szShow13 db SM_CYBORDER,0 szShow14 db Window border height,0 szShow15 db SM_CXFIXED
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 樂(lè)清2022年事業(yè)編招聘考試模擬試題及答案解析16
- 2026屆遼寧省葫蘆島市高三上學(xué)期期末考試歷史試題(含答案)
- 邵陽(yáng)職院考試題庫(kù)及答案
- 鉗工知識(shí)競(jìng)賽試題及答案
- 辯論培訓(xùn)課件
- 北師大版數(shù)學(xué)三年級(jí)上冊(cè)期末評(píng)價(jià)(A卷)(含答案)
- 四川省綿陽(yáng)市游仙區(qū)2024-2025學(xué)年八年級(jí)上學(xué)期期末地理試題(含答案)
- 輔警特色培訓(xùn)課程
- 2025 小學(xué)三年級(jí)科學(xué)下冊(cè)保護(hù)植物的重要性教育課件
- 2026年深圳中考語(yǔ)文考前15天沖刺試卷(附答案可下載)
- GB/T 11018.1-2008絲包銅繞組線第1部分:絲包單線
- GB 31633-2014食品安全國(guó)家標(biāo)準(zhǔn)食品添加劑氫氣
- GA/T 765-2020人血紅蛋白檢測(cè)金標(biāo)試劑條法
- 武漢市空調(diào)工程畢業(yè)設(shè)計(jì)說(shuō)明書(shū)正文
- 麻風(fēng)病防治知識(shí)課件整理
- 消防工程監(jiān)理實(shí)施細(xì)則
- 安全安全應(yīng)急救援預(yù)案(溝槽開(kāi)挖)
- 權(quán)利的游戲雙語(yǔ)劇本-第Ⅰ季
- 衛(wèi)生部《臭氧消毒技術(shù)規(guī)范》
- 早期復(fù)極綜合征的再認(rèn)識(shí)
- 山西某2×150MW循環(huán)流化床空冷機(jī)組施工組織設(shè)計(jì)方案
評(píng)論
0/150
提交評(píng)論