Windows編程 第六回 又見設備描述表

設備描述表還真是十分重要,以至於我們在這裏還要再學習它。這節內容比較少,相信你很快就應該能看完。Let’go!

獲取設備描述表句柄

複習:當你想在一個圖形輸出設備(諸如屏幕或者打印機)上繪圖時,你首先必須獲得一個設備描述表(或者DC)的句柄。將句柄傳回給程序 時,Windows就給了你使用設備的權限。然後你在GDI函數中將這個句柄作爲一個參數,向Windows標識你想在其上進行繪圖的設備。如果在處理一 個消息時取得了設備描述表句柄,應該在退出窗口函數之前釋放它(或者刪除它)。一旦釋放了句柄,它就不再有效了。

Windows提供了幾種取得設備描述表句柄的方法,如下。

方法一:

    這是獲取並釋放設備描述表句柄最常用的方法,即在處理WM_PAINT消息時使用BeginPaint和EndPaint函數。這兩個函數需要程序的窗口 句柄和PAINTSTRUCT結構的變量的地址爲參數。Windows程序員通常把這一結構變量命名爲ps並且在窗口過程中定義它:

PAINTSTRUCT ps ;

在處理WM_PAINT消息時,窗口過程首先調用BeginPaint。一般在BeginPaint函數的調用中,如果 客戶區的無效區域的背景還未被擦除,則由Windows來擦除。(它使用已註冊的窗口類WNDCLASS結構的hbrBackground成員變量中指定 的畫刷來擦除背景,上回的註釋1我們提過)

BeginPaint調用使整個客戶區有效,並且返回一個設備描述表句柄,這一返回值通常被保存在叫做hdc的變量中。該函數也填入ps結構的字段(可以回憶一下上一回的註釋1)。

設備描述表句柄在窗口過程中的定義如下:

HDC hdc ;//定義設備描述表句柄變量

然後,程序就可以使用需要設備描述表句柄的TextOut等GDI函數。在窗口的客戶區顯示文字和圖形需要設備描述表句柄,但是從BeginPaint返回的設備描述表句柄不能在客戶區域之外繪圖。調用EndPaint即可釋放設備描述表句柄。

一般地,處理WM_PAINT消息的形式如下:

caseWM_PAINT:

  hdc = BeginPaint (hwnd, &ps) ;

         [other program lines]

  EndPaint (hwnd, &ps) ;

  return 0 ;

我們再來強調一下吧(別怪我囉嗦):BeginPaint調用使整個客戶區有效(阻止WM_PAINT消息一直髮送,見 附錄),填充ps結構的字段,返回的設備描述表句柄。使用這個句柄只能在ps結構中的rcPaint字段規定的區域內繪圖。EndPaint調用可釋放設 備描述表句柄。兩個函數必須成對使用,一般只用在處理WM_PAINT消息中。

 

Windows程序在處理非WM_PAINT消息時還可以用以下方法獲得設備描述表句柄,當然以下函數調用不會使客戶區中的無效區域變成有效。

方法二:

hdc = GetDC (hwnd) ;
[other program lines]        
ReleaseDC (hwnd, hdc) ;
GetDC函數調用後會返回hwnd參數所指定的窗口的客戶區所對應的設備描述表句柄。可見GetDC調用與BeginPaint的基本區別是,利用從GetDC返回的句柄可以在整個客戶區上繪圖。如果hwnd參數設置爲NULL,那麼函數會返回整個桌面的設備描述表句柄。當不再需要該設備環境時,需要調用ReleaseDC函數釋放設備描述表。
 

方法三:

hdc = GetWindowDC (hwnd) ;

[other program lines]         
ReleaseDC (hwnd, hdc) ;
GetWindowDC返回可以在整個窗口(包括客戶區部分和標題欄、菜單、滾動條、框架等非客戶區部分)繪圖的設備描述表句柄,不過此函數很少使用。當不再需要該設備描述表時,需要調用ReleaseDC函數釋放設備描述表。
 

方法四:

hdc =CreateDC (lpszDriver, lpszDevice, lpszOutput, lpData) ;

       [other program lines] 
DeleteDC (hdc) ;
BeginPaint、GetDC和GetWindowDC獲得的設備內容都與顯示器上的某個特定窗口(即hwnd)相關。CreateDC是取得設備描述表句柄一個更通用的函數,它甚至可以獲取非顯示器輸出設備描述表句柄。當不再需要該設備描述表時只可調用DeleteDC函數刪除它。

當然,獲得設備描述表的函數還有很多,我們就先簡單介紹到這兒,其他類似的函數我們以後遇到再說。

設備描述表的屬性——我們上回提到過哦

設備描述表中包含許多確定GDI函數如何在設備上工作的當前“屬性”,這些屬性允許傳遞給GDI函數的參數只包含起始座標或者尺寸信息,而不必包含 Windows在設備上顯示對象時需要的所有其它信息。例如,上回我們調用TextOut時,我們只需要在函數中給出設備描述表句柄、起始座標、文字和文 字的長度。而不必指定字體、文字顏色、文字後面的背景色彩以及字符間距,因爲這些屬性都是設備描述表的一部分。當你想改變這些屬性之一時,您調用一個可以 改變設備描述表中屬性的函數(例如把文字顏色設置成紅色),以後針對該設備描述表的TextOut調用

就可以使用改變後的屬性了(例如可以輸出紅色字體)。

我在此列出如下表,方便大家查閱。

 

屬性

默認值

相關函數

背景色

WHITE

GetBkColor

SetBkColor

背景模式

OPAQUE

GetBkMode

SetBkMode

位圖

NONE

CreateBitMap

CreateBitMapIndirect

CreateCompatibleBitmap

SelectObject

畫刷

WHITE_BRUSH

CreateBrushIndirect

CreateDIBPatternBrush

CreateHatchBrush

CreatePatternBrush

CreateSolidBrush

SelectObject

畫刷起始位置

(0,0)

GetBrushOrg

SetBrushOrg

UnrealizeObject

剪裁域

DISPLAY SURFACE

ExcludeClipRect

IntersetClipRect

OffsetClipRgn

SelectClipPath

SelectObject

SelectClipRgn

顏色調色板

DEFAULT_PALETTE

CreatePalette

RealizePatte

SelectPalette

繪圖方式

R2_COPYPEN

GetROP2

SetROP2

字體

SYSTEM_FONT

CreateFont

CreateFontIndirect

SelectObject

字符間距

0

GetTextCharacterExtra

SetTextCharacterExtra

映射方式

MM_TEXT

GetMapMode

SetMapMode

畫筆

BLACK_PEN

CreatePen

CreatePenIndirect

SelectObject

多邊形填充方式

ALTERNATE

GetPolyFillMode

SetPolyFileMode

縮放模式

BLACKONWHITE

SetStretchBltMode

GetStretchBltMode

文本顏色

BLACK

GetTextColor

SetTextColor

視圖範圍

(1,1)

GetViewportExtEx

SetViewportExtEx

ScaleViewportExtEx

視圖原點

(0,0)

GetViewportOrgEx

   

SetViewportOrgEx

窗口範圍

(1,1)

GetWindowExtEx

SetWindowExtEx

ScaleWindowExtEx

窗口原點

(0,0)

GetWindowOrgEx

OffsetWindowOrgEx

SetWindowOrgEx

一般以Get開頭的函數作用是獲取該屬性值,一般以Set開頭的函數作用是設置該屬性值,若有不太清楚的函數,你就自己動手查吧。

保存設備描述表

通常,在你調用GetDC或BeginPaint時,Windows用默認值建立一個新的設備描述表,你對屬性所做的一切改變在設備描述表調用 ReleaseDC或EndPaint釋放時,都會丟失。如果你的程序需要使用非默認的設備描述表屬性,則你必須在每次取得設備描述表句柄時初始化設備內 容:

case WM_PAINT:
    hdc = BeginPaint (hwnd, &ps) ;
          [initialize device context attributes]
          [paint client area of windows]
    EndPaint (hwnd, &ps) ;
    return 0 ;

雖然在通常情況下這種方法已經很令人滿意了,但是你還可能想要在釋放設備描述表之後,仍然保存程序中對設備描述表屬性所做的改變,以便在下一次調用 GetDC和BeginPaint時它們仍然能夠起作用。爲此,可在設計窗口類時,將CS_OWNDC標誌包含爲窗口類的一部分:

wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ;

現在,基於這個窗口類所創建的每個窗口都將擁有自己的設備描述表,它一直存在,直到窗口被刪除。如果使用了CS_OWNDC風格,就只需初始化設備描述表一次,可以在處理WM_CREATE消息處理期間完成這一操作:

case WM_CREATE:
     hdc = GetDC (hwnd) ;
          [initialize device context attributes]       
ReleaseDC (hwnd, hdc) ;

這些屬性在改變之前一直有效。

CS_OWNDC風格影響GetDC和BeginPaint獲得的設備內容,不影響其它函數獲得的設備描述表。使用了CS_OWNDC,你仍然應該在退出窗口過程之前釋放設備描述表。

某些情況下,你可能想改變某些設備描述表屬性,用改變後的屬性進行繪圖,然後恢復原來的設備描述表屬性。要簡化這一過程,可以通過如下調用來保存設備描述表的狀態:

int idSaved = SaveDC (hdc) ;
    現在,可以改變一些屬性,在想要回到調用SaveDC前存在的設備內容時,調用:
RestoreDC (hdc, idSaved) ;
    你可以在調用RestoreDC之前調用SaveDC數次。

大多數程序寫作者以不同的方式使用SaveDC和RestoreDC。然而,更像彙編語言中的PUSH和POP指令,當你調用SaveDC時,可以不需要保存返回值:

SaveDC (hdc) ;
    然後,你可以更改某些屬性並再次調用SaveDC。要將設備內容恢復到一個已經保存的狀態,調用:
RestoreDC (hdc, -1) ;
    這就將設備描述表恢復到最近由SaveDC函數保存的狀態中。

 

附錄

小技巧:

在處理WM_PAINT消息時,爲了在更新的矩形外繪圖,可以在調用BeginPaint之前使用如下函數:

InvalidateRect (hwnd, NULL, TRUE) ;
它使整個顯示區域變爲無效,並擦除背景。但是,如果最後一個參數等於FALSE,則不擦除背景,原有的東西將保留在原處。

通常這是Windows程序在無論何時收到WM_PAINT消息而不考慮rcPaint結構的情況下簡單地重畫整個客戶區最方便的方法。例如,如果 在客戶區的顯示輸出中包括了一個圓,但是隻有圓的一部分落到了無效矩形中,它就僅繪製圓在無效矩形中的部分。這對於畫整個圓來說是無意義的。注意:使用從 BeginPaint返回的設備描述表句柄時,Windows不會繪製rcPaint矩形外的任何部分。在BeginPaint調用之前使用上面那個函 數,可使rcPaint結構中的無效矩形爲整個客戶區,就不愁繪製圓了。

在處理WM_PAINT消息時,必須成對地調用BeginPaint和EndPaint。如果窗口過程不處理 WM_PAINT消息,則它必須將WM_PAINT消息傳遞給Windows中DefWindowProc(默認窗口過程)。DefWindowProc 以下列代碼處理WM_PAINT消息:

case WM_PAINT:

            BeginPaint (hwnd, &ps) ;

            EndPaint (hwnd, &ps) ;

           return 0 ;

    這兩個BeginPaint和EndPaint調用之間中沒有任何語句,僅僅使先前無效區域變爲有效。但以下方法是錯誤的:

case WM_PAINT:

    return 0 ;  // WRONG !!!

    Windows將一個WM_PAINT消息放到消息隊列中,是因爲客戶區的一部分無效。如果不調用BeginPaint和EndPaint(或者 ValidateRect),則Windows不會使該區域變爲有效。若一直保持無效,Windows將會再發送另一個WM_PAINT消息,且一直髮送 下去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章