亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

Windows編程基礎 - 設備對象屬性

2019-11-17 05:18:28
字體:
來源:轉載
供稿:網友

  設備對象屬性決定著繪圖方式,當使用GDI函數繪圖時,所繪制的圖形和文本的顏色、大小和位置等由設備對象的當前屬性決定,應用程序可以使用GDI函數改變設備對象的當前屬性。本章介紹設備對象的一些主要屬性和用于改變這些屬性的有關函數,其它的設備對象屬性在后面的章節中陸續介紹。

3.1、圖形設備接口
  在第二章,我們一直在使用圖形設備接口(GDI),GDI的主要目標之一是支持在輸出設備(例如顯示器、打印機)上建立與設備無關的圖形輸出。Windows的圖形大部分是由GDI.EXE(Windows系統的一個模塊,稱GDI模塊)中的函數處理的。GDI模塊通過調用在不同設備驅動程序模塊中的例程來控制輸出設備。例如,顯示設備驅動程序用于訪問與視頻顯示器有關的硬件。通過GDI,Windows可以確定驅動程序能夠進行什么工作,并且由于應用程序只與GDI打交道。這樣,GDI通過將應用程序與不同的輸出設備隔離,使應用程序可以在支持Windows的任何圖形輸出設備上工作。
  圖形輸出設備可以分為兩組:光柵設備和矢量設備。光柵設備將圖象表示為點(象素)的圖案。這類輸出設備包括顯示設備、點陣打印機和激光打印機。向量設備用線段來繪制圖象,例如繪圖儀。Windows的GDI是一種隔離了硬件具體特性的圖形語言。雖然輸出設備用象素來表示圖形,但GDI卻可以被用作一個高級的向量繪圖系統,也可以被用來進行較低級的象素操作。
  在編寫Windows應用程序時,程序員不必為顏色過分擔心,假如在應用程序中使用的一種顏色不能被該顯示器所表示,Windows或為應用程序選擇一種最直接的純顏色(顯示設備可以表示的顏色),或通過將幾種純顏色相混合來表示這種顏色。當在彩色顯示器上開發的程序運行在單色顯示器上時,Windows將使用灰度來表示顏色。應用程序也可以在程序中確定輸出設備的有關特性,例如,可表示的顏色數目、設備的顯示區的尺寸等,以便最大限度地發揮硬件的能力。

3.2、設備對象屬性
  設備對象具有許多決定GDI函數在設備對象上如何工作的當前屬性。例如,在使用函數TextOut()時,只需要在函數中說明設備對象的句柄、繪制字符的起始坐標、文本和文本長度,而不用說明字體、文本顏色、背景顏色和字符間距等,因為這些特征由設備對象的屬性決定。每種設備對象都賦有缺省的屬性,可以使用GDI函數改變這些屬性中的某一個。表3-1給出了顯示設備各個屬性的缺省值,當使用GetDC()和BeginPaint()等函數初次得到一個顯示設備對象時,該對象的屬性具有缺省值。

屬性缺省值視區原點(0, 0)視區范圍(1, 1)窗口原點(0, 0)窗口范圍(1, 1)背景顏色白色背景方式OPAQUE位圖任意值刷子WHITE_BRUSH刷子原點(0, 0)裁剪區用戶區/無效矩形區/子窗口區調色板DEFAULT_PALETTE 屬性缺省值筆的當前位置(0, 0)筆的顏色BLACK_PEN文本顏色黑設備的原點用戶區的左上角繪圖方式R2_COPYPEN字體SYSTEM_FONT字符間距0映射方式MM_TEXT多邊形填充方式ALTERNATE相對一絕坐標ABSOLUTE縮放方式BLACKONWHITE
  在本章以后的各節中將介紹其中一些設備屬性,其它屬性在以后章節中介紹。

3.3、設備坐標系
  為在輸出設備上定位和繪制圖形對象,必須引入一種坐標系。Windows的各種不同類型設備所使用的坐標稱為設備坐標。它們使用笛卡爾坐標系,在這些設備坐標系中,單位都以象素的個數表示(稱為設備單位)。x軸上的值自左向右增加,y軸上的值自頂向下增加,見圖3-1。

Windows編程基礎 - 設備對象屬性(圖一)圖3-1 Windows的設備坐標系
  本節以視頻顯示設備為例介紹Windows的設備坐標系,其中的許多內容也適合用于象打印機等硬拷貝輸出設備。
  在Windows環境中,視頻顯示設備是一個共享設備,即在同一時刻,顯示設備上可以同時顯示多個應用程序的輸出信息。為了保護一個程序顯示的信息不被其他程序破壞,Windows通過將顯示區看作不同的設備對象來限制應用程序輸出信息的范圍。
  一個應用程序可獲取三種不同的顯示設備對象句柄,每種句柄所標識的設備對象代表屏幕上的不同區域。我們可以將這三個句柄所標識的對象視作三個不同的抽象顯示設備,都帶有如圖3-1所示的設備坐標系,但對不同的抽象設備,坐標原點不一樣的。這樣,當使用相同的起始坐標而使用不同的設備對象句柄調用GetDC函數(例如TextOut())進行繪圖時,信息顯示的位置不一樣。
  第一抽象設備是用戶區對象,它的坐標原點在用戶區的左上角。定位該區域的設備坐標系稱為用戶區坐標系。使用函數GetDC()或BeginPaint()得到的句柄是標識用戶區的句柄。當使用該句柄標識該區域的句柄作為GDI函數的參數時,GDI函數所使用的坐標值是相對于用戶區坐標系。
  第二個抽象設備是全窗口對象。它包括標題欄、選單、滾動桿和窗口框架等。定位這個區域的坐標系稱為全窗口坐標系,它的原點的左上角。使用函數GetWindowsDC()可以獲得該設備對象的句柄,然后通過該句柄使用全窗口坐標系在該區域中繪圖。應用程序一般不在這個區域中繪圖。
  第三個抽象設備是整個屏幕對象,其坐標原點在屏幕的左上角,定位該區域的設備坐標系稱為屏幕坐標系。使用語句:

  HDC hDC = CreateDC("DISPLAY", NULL, NULL, NULL);

可以獲得該設備對象的句柄,使用該句柄的GDI函數所使用的坐標是相對于屏幕坐標系。
  這三種坐標系方便了程序在不同的區域繪制圖形的需要。例如,由于使用用戶區坐標系,即使窗口在屏幕上被移動到其他位置,但用戶區中顯示的信息相對于用戶坐標系而言其坐標值不變。
  使用坐標系,解決了顯示對象的定位的問題的。但以象素單位所建立的坐標系不符合用戶(或程序員)的習慣,而且在不同分辨率的輸出設備上,一個象素的大小也不相同的,這樣,應用程序的輸出在不同機器上會得到不同的結果。為解決這個問題,Windows引入了邏輯坐標系。它使得用戶可以按照自己習慣使用的尺寸(毫米、英寸等)來描述客體,或繪制圖形。用戶在程序中使用邏輯坐標系中的坐標值,而由Windows根據映射方式將邏輯坐標系中的坐標值轉算成設備坐標系中的坐標值(對于顯示設備而言,具體映射到三個坐標系中哪一個取決于GDI函數中所使用的設備對象句柄)。映射方式也同時決定著邏輯坐標值的單位(簡稱邏輯單位)和坐標軸的正方向。在前面介紹的TextOut和DrawText()使用的是邏輯單位,而函數CreateWindow()使用的是屏幕坐標系中的坐標(設備單位,及象素)。程序員應該清楚什么時候在使用邏輯單位。一般而言,當映射方式起作用時,使用的是邏輯單位,而映射方式起作用的唯一時候是使用了那些需要將設備對象句柄作為參數的GDI函數的時候,但并非總是這樣,因此,讀者應注重這方面的問題。
  表3-2給出的兩個函數用于進行用戶區坐標和屏幕坐標(注重是設備單位)之間的轉換。

  表3-2-1 ClientToScreen 函數
用 途將一個點的用戶區坐標轉換為屏幕坐標。原 型VOID ClientToScreen(    HWND hWnd, 與用戶區相關聯的窗口句柄  LPPOINT lpPoint 指向一個POINT結構類型的變量,其中一個點的用戶區坐標值,并被轉換后的屏幕坐標值取代。); 
  表3-2-2 ScreenToClient 函數
用 途將給定點的屏幕坐標轉換為用戶區坐標。原 型VOID ScreenToClient(    HWND hWnd, 與用戶區相關聯的窗口句柄  LPPOINT lpPoint 所指向的變量包含一個點的屏幕坐標值,并被轉換后的用戶區坐標值取代); 
3.4、映射方式
  影響在用戶區繪圖的一個主要設備對象屬性是“映射方式”。它定義了Windows如何將GDI函數中使用的邏輯坐標映射為設備坐標。其他四個設備對象屬性(窗口原點、視區原點、窗口范圍和視區范圍)都與映射方式密切相關。當Windows將邏輯單位轉換為設備單位或象素時,映射方式、窗口原點和視區原點、窗口范圍和視區反問決定著這種轉換。映射方式也隱含了x軸坐標和y軸的原點和方向。在介紹各種映射方式之前,我們先介紹窗口與視口關系。

  3.4.1 窗口與視口
  視口是計算機屏幕上一塊顯示區域,隨著在GDI中所使用的設備對象句柄的不同,該區域可是用戶區、全窗口區或整個屏幕區,視區中的圖形一設備單位定義。與視區中顯示的圖形相對應的原始圖形區域稱為窗口。注重,在這里使用的術語“窗口”不是指屏幕上顯示的可視窗口對象。這里的“窗口”是從現實世界角度所看到圖形,而“視區”是從數據世界角度而言的,是屏幕上的象素形成的圖形。在Windows中,視區不是一個裁剪區。圖3-2和圖3-3說明了窗口和視口的關系。

Windows編程基礎 - 設備對象屬性(圖二)圖3-2 營業收入曲線 Windows編程基礎 - 設備對象屬性(圖三)圖3-3 視區中顯示的曲線
  映射方式指的是從“窗口”(邏輯坐標)到“視區”(設備坐標)的變換。“視區”采用設備坐標(象素數)?!按翱凇辈捎眠壿嬜鴺耍梢允窍笏財?、毫米、英寸或任何其他單位。GDI函數使用邏輯坐標。
  將“窗口”(邏輯)坐標轉換為“視區”(設備)坐標,使用如下兩個映射方式:

xViewport = (xWindow - xWinOrg) * xViewExt / xWinExt + xViewOrg
yViewport = (yWindow - yWinOrg) * yViewExt / yWinExt + yViewOrg

  該公式將邏輯坐標系中的點(xWindow, yWindow)變換為設備坐標系中的點(xViewport, yViewport),其中,點(xWinOrg, yWinOrg)是以邏輯單位表示的“窗口”原點,而點(xViewOrg, yViewOrg)是以設備坐標表示的“視區”原點。在缺省情況下,這兩個點被設置為(0, 0),但它們可以被改變。注重,邏輯點(xWinOrg, yWinOrg)總是被映射為設備點(xViewOrg, yViewOrg)。
  改變上述的公式為:

xWindow = (xViewport - xViewOrg) * xWinExt / xViewExt + xWinOrg
yWindow = (yViewport - yViewOrg) * yWinExt / xViewExt + yWinOrg

  該公式可以將視區坐標轉換為窗口坐標。

  表3-3-1 DptoLP 函數
用 途將設備點變換為邏輯點。原 型BOOL DPtoLP(   HDC hDC,設備對象句柄  LPPOINT lpPoints, 指向POINT類型的變量的指針  int nCount要進行變換的點的數目) 返回值若變換成功,返回非零。
  表3-3-2 LPtoDP 函數
用 途將邏輯點變換為設備點。原 型BOOL LPtoDP(   HDC hDC,設備對象句柄  LPPOINT lpPoints, 指向POINT類型的變量的指針  int nCount欲進行變換的點的數目) 返回值若所有的點被變換,返回非零。
  表3-3給出了兩個函數,用于進行設備坐標和邏輯坐標之間的相互變換。例如,函數GetClientRect獲取的擁護區域的大小總是以設備單位表示的,若想使用邏輯單位表示用戶區大小,可以使用函數DPtoLP();

  RECT rect;
  GetClient(hWnd, &rect);
  DPtoLP(hDC, (LPPOINT)&rect, 2);

  3.4.2 Windows的映射方式
  Windows定義了八種映射方式,見表3-4。

  表3-4 Windows的映射方式    映射方式邏輯單位單位x軸方向y軸方向MM_TEXT象素數向右向下MM_LOMETRIC0.1mm向右向上MM_HIMETRIC0.01mm向右向上MM_LOENGLISH0.01英寸向右向上MM_HIENGLISH0.001英寸向右向上MM_TWipS1/1440英寸向右向上MM_ISOTROPIC自定義(x=y)(即x和y的邏輯單位大小一樣)由比例因子決定若為正,向右。否則,向左由比例因子決定若為正,向下。否則,向上MM_ANISOTROPIC自定義(x!=y)(即x和y的邏輯單位大小不一樣)同上同上

  注:Twips表示“一個點的二十分之一”,在GDI中,一個點的大小定為1/72英寸,所以一個twips是1/1440英寸。
  在缺省時,設備對象使用的映射方式是MM_TEXT,即邏輯單位等于物理單位。例如:

  TextOut(hDC, 4, 6, "Hello", 5);

  在用戶區向右偏移4個象素,向下偏移6個象素的位置開始顯示信息“Hello”。
  可以使用函數SetMapMode()設置映射方式,或使用函數GetMapMode()獲取一個設備對象當前的映射方式,見表3-5,例如,語句:

  SetMapMode(hDC, MM_LOMETRIC);
  TextOut(hDC, 100, -200, "Hello", 5);

  首先設置MM_LOMETRIC映射方式,然后在離用戶區原點向右1厘米、向下2厘米的位置顯示信息“Hello”。這里使用了負的坐標值,因為根據窗口原點變換到視口原點的原則,只有窗口坐標的y坐標為負值的點才可以變換到用戶區中,y坐標為正值的點落在用戶區之外。

  表3-5-1 SetMapMode 函數
用 途設置設備對象的映射方式。原 型int SetMapMode(   HDC hDC,設備對象句柄  int nMapMode 所欲設置的映射方式,使用表3-4中常量之一) 返回值先前的映射方式。
  表3-5-1 GetMapMode 函數
用 途檢索當前的映射方式。原 型int GetMapMode(   HDC hDC,設備對象句柄) 返回值當前的映射方式。
  Windows函數中指定的所有坐標值必須在-32768到32767之間的值(這是C語言的int類型的數據的值域)。
  在任何映射方式下,邏輯坐標和物理坐標的缺省原點都為(0, 0),即邏輯坐標的點(0, 0)映射到設備坐標的點(0, 0)。

  3.4.3 改變視區和窗口的原點
  表3-6說明的函數可用于改變視區和窗口的原點,但這兩個函數不能同時使用。

  表3-6-1 SetViewportOrg 函數
用 途設置視區的坐標原點。原 型DWord SetViewportOrg(   HDC hDC,設備對象句柄  int x,指定視區坐標原點的x坐標值(設備單位)?! nt y,指定視區坐標原點的y坐標值(設備單位)。) 返回值返回以前視區坐標原點(設備單位),高位字為y坐標,低位字為x坐標。
  表3-6-2 SetWindowOrg 函數
用 途設置窗口坐標的原點。原 型DWORD SetWindowOrg(   HDC hDC,設備對象句柄  int x,指定窗口坐標原點的x坐標值(邏輯單位)?! nt y,指定窗口坐標原點的y坐標值(邏輯單位)。) 返回值返回以前視區坐標原點(設備單位),高位字為y坐標,低位字為x坐標。
  無論怎樣改變窗口和視區的原點,Windows都將窗口原點變換到視區原點,并按同樣的映射算法(即使用當前給出的映射公式)變換其余的點。
  我們先介紹MM_TEXT映射方式下這兩個函數的工作原理。因為這種映射方式的x坐標和y坐標方向與我們閱讀文本時的方向一致。因此,MM_TEXT映射方式又稱為“文本”映射方式。

Windows編程基礎 - 設備對象屬性(圖四)圖3-4
  現假定用戶區的寬度為xClient個象素,高為yClient給象素,則語句:

  SetViewportOrg(hDC, xClient/2, yClient/2);

  將邏輯點(0, 0)確定在(映射到)用戶區的中心。

Windows編程基礎 - 設備對象屬性(圖五)圖3-5
  這時,若使用語句:

  TextOut(hDC, 0, 0, "Hello", 5);

  將在用戶區中心位置開始顯示信息。而語句:

  TextOut(hDC, -xClient/2, -yClinent/2, "Hello", 5);

  在用戶區的左上角(設備點(0, 0))處開始顯示文本。
  通過使用函數SetWindowOrg可以得到與使用函數SetViewpostOrg相同的結果,例如:

  SetWindowOrg(hDC, -xClient/2, -yClient/2);

  這使得邏輯點(-xClient/2, -yClient/2)被映射為設備點(0, 0)。
  函數GetViewportOrg和GetWindowOrg可分為別用于獲取當前視區和窗口的原點,見表3-7。

  表3-7-1 GetViewportOrg 函數
用 途獲取當前的視區原點。原 型DWORD GetViewportOrg(   HDC hDC,設備對象句柄) 返回值視區的原點(設備坐標),y坐標在高位字中,x坐標在低位字中。
  表3-7-2 GetWindowOrg 函數
用 途獲取當前的視區原點。原 型DWORD GetWindowOrg(   HDC hDC,設備對象句柄) 返回值窗口的原點,y坐標在高位字中,x坐標在低位字中。
  Windows的映射方式中,MM_LOMETRIC、MM_HIMETRIC、MM_LOENGLISH、MM_HIENGLISH和MM_TWIPS是以物理度量單位表示邏輯坐標的映射方式。這五種映射方式被稱為公制映射方式,因為它們在X軸和Y軸上的邏輯坐標被映射為等量的物理單位,所以這些映射方式便于繪制圓或正方形。
  當初次設置這些映射方式之一時,坐標系為:

Windows編程基礎 - 設備對象屬性(圖六)圖3-6
  因此,在用戶區中顯示信息必須使用負的Y坐標值。例如:

  SetMapMode(hDC, MM_LOENGLISH);
  TexOut(hDC, 100, -100, "Hello", 5);

  將在用戶區向右偏移1英寸,向下偏移1英寸的位置顯示“Hello”。
  下面的語句將邏輯點(0, 0)設置在用戶區的左下角:

  SetViewportOrg(hDC, 0, yClient);

Windows編程基礎 - 設備對象屬性(圖七)圖3-7
  而下面的語句將邏輯點(0, 0)設置在用戶區的中心點。

  SetViewportOrg(hDC, xClient/2, yClient/2);

Windows編程基礎 - 設備對象屬性(圖八)圖3-8
  在Windows提供的八種映射方式中,唯有MM_ISOTROPIC和MM_ANISOTROIC答應用戶改變窗口和視口的范圍(其他六種的范圍是固定的,用戶不能改變)。表3-7列出了與窗口范圍和視區范圍有關的函數。

  表3-7-3 SetWindowExt 函數
用 途設置窗口的x和y坐標范圍。原 型DWORD SetWindowExt(   HDC hDC,設備對象句柄  int x,窗口的x坐標范圍(邏輯單位)  int y窗口的y坐標范圍(邏輯單位)) 返回值以前的坐標范圍(邏輯單位),y坐標范圍在高位字中,x坐標范圍在低位字中。
  表3-7-4 GetWindowExt 函數
用 途檢索窗口的x和y坐標范圍。原 型DWORD GetWindowExt(   HDC hDC,設備對象句柄) 返回值y范圍在高位字中,x范圍在低位字中。
  表3-7-5 SetViewportExt 函數
用 途設置視區的x和y范圍。原 型DWORD SetViewportExt(   HDC hDC,設備對象句柄  int x,視區的x坐標范圍(設備單位)  int y視區的y坐標范圍(設備單位)) 返回值以前的視區的坐標范圍(設備單位),y在高位字中,x在低位字中。
  表3-7-6 GetViewportExt 函數
用 途設置視區的x和y范圍。原 型DWORD GetViewportExt(   HDC hDC,設備對象句柄) 返回值視區的X和Y坐標范圍(設備單位),y在高位字中,x在低位字中。
  窗口和視區的范圍不具有任何裁剪區的意義,它們用于確定將邏輯坐標系中的單位放大或縮小多少以適合設備坐標系中的單位。例如,假如窗口的X坐標范圍是2,而視區的X坐標范圍是4,則GDI將兩個邏輯單位(按X軸計算)映射為一個設備單位。
  坐標范圍也同樣決定了兩個坐標系(窗口和視區)的X軸和Y軸的相對定向關系。假如相關聯的窗口和視區范圍符號一致,則坐標軸方向相同,否則方向相反。例如,假如窗口和視區的X坐標范圍分別是2和4,則GDI將邏輯坐標系的X軸的正半軸映射到該設備坐標系統的X軸正半軸;假如窗口和視區的Y坐標范圍分別為2和-1,則GDI將邏輯坐標系統的Y軸的正半軸映射到設備坐標系的Y軸的負半軸。
  對于MM_ISOTROPIC映射方式,詞“isotropic”表示在所有的方向都“相等”,即該方式的用途是在兩個軸上保持相等的邏輯單位。我們舉一例子來說明該方式的設置方法。
  我們設置這樣一個坐標系,(0, 0)點位于用戶區域的左下角,寬度范圍從0到32767,高度范圍也從0到32767:

Windows編程基礎 - 設備對象屬性(圖九)圖3-9
并使用下列程序段設置映射方式和范圍:

  SetMapMode(hDC, MM_ISOTROPIC);
  SetWindowExt(hDC, 32767, 32767);
  SetViewportExt(hDC, xClient, yClient);  // xClient和yClient是用戶區的寬度和高度
  SetViewportOrg(hDC, 0, yClient);

  這時,Windows將調整范圍,使兩個軸上的邏輯單位表示相同的物理距離。假如用戶區的寬度比高要長,則Windows調整x范圍。這時坐標位置如下圖:

Windows編程基礎 - 設備對象屬性(圖十)圖3-10
  邏輯窗口將位于用戶區左側,并且由于Windows的坐標值不能大于32767,就無法在用戶區的右側部分顯示任何圖形。假如用戶的高度比寬度要長,則Windows調整y的范圍,這時候坐標位置如下圖:

Windows編程基礎 - 設備對象屬性(圖十)圖3-11
  同樣的原因,在用戶的頂部也無法顯示任何圖形,因為這時需要大于32767邏輯單位的y坐標。
  由此可見,對于MM_ISOTROPIC映射方式,程序員可以調用函數SetViewportExt設置范圍,Windows通過調整范圍,使得兩個軸上每一個邏輯單位表示相同的物理距離,這對于繪制圓和正方形等是方便的。
  對函數SetViewportExt的調用必須在對函數SetViewportExt的調用之前進行,這可以最有效地使用用戶區中的空間。若不是這樣的話,當Windows調整范圍時,為了使邏輯窗口置于物理視區內,可能導致用戶區域的某一部分在邏輯窗口的外面。
  窗口和視區的范圍不表示裁剪區,我們分析下面的程序片斷所建立的坐標系:

  SetMapMode(hDC, MM_ISOTROPIC);
  SetWindowExt(hDC, 1000, 1000);
  SetViewportExt(hDC, xClient/2, -yClient/2);
  SetViewportExt(hDC, xClient/2, yClient/2);

  我們分析用戶區寬度大于高度的情況:

Windows編程基礎 - 設備對象屬性(圖六)圖3-12
  對于上圖所示的情況,若點的X坐標小于-1000或大于+1000,則這個點可能落在用戶區內,也可能落在用戶區之外;但對于Y坐標小于-1000或大于+1000的點,這個點一定落在用戶區之外。
  最后,我們介紹MM_ANISOTROPIC映射方式,單詞“anisotropic”的含義是“在所有的方向上不相等”。對于MM_ANISOTROPIC,Windows對所設置的范圍不進行調整,這表明,對于MM_ANISOTROPIC,兩個坐標軸上的邏輯單位具有不同的物理尺寸。例如,對于上面的例子,假如設置為MM_ANISOTROPIC映射方式。

  SetMapMode(hDC, MM_ANISOTROPIC);
  SetWindowExt(hDC, 1000, 1000);
  SetViewportExt(hDC, xClient/2, -yClient/2);
  SetViewportExt(hDC, xClient/2, yClient/2);

  則所建立的坐標系如下圖所示:

Windows編程基礎 - 設備對象屬性(圖六)圖3-13

  下面的程序實例是使用MM_ANISOTROPIC映射方式重新設計的2.5節的程序。

  // 3-1.c
  #include <stdio.h>
  #include <windows.h>

  LRESULT CALLBACK WndPRoc(HWND, UINT, WPARAM, LPARAM);

  int PASCAL WinMain(
    HINSTANCE hInstance,   // 應用程序的實例句柄
    HINSTANCE hPrevInstance, // 該應用程序前一個實例的句柄
    LPSTR lpszCmdLine,    // 命令行參數串
    int nCmdShow )      // 程序在初始化時如何顯示窗口
  {
     char szAppName[] = "DispText";
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

    if (!hPrevInstance) {
      // 該實例是程序的第一個實例,注冊窗口類
      wndclass.style = CS_VREDRAW CS_HREDRAW;
      wndclass.lpfnWndProc = WndProc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hInstance = hInstance;
      wndclass.hIcon = LoadIcon(hInstance, IDI_application);
      wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
      wndclass.lpszMenuName = NULL;
      wndclass.lpszClassName = szAppName;

      if (!RegisterClass(&wndclass))
        // 假如注冊失敗
        return FALSE;
    }

    // 對每個實例,創建一個窗口對象
    hwnd = CreateWindow(
      szAppName,
      "Display Text",
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT,
      CW_USEDEFAULT, CW_USEDEFAULT,
      NULL,
      NULL,
      hInstance,
      NULL );

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while( GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }

    return msg.wParam;
  }

  LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    static int xChar, yChar;
    int line;
    char szBuffer[256];
    HDC hDC;
    char ch;
    PAINTSTRUCT ps;
    TEXTMETRIC tm;
    FILE *fp;

    switch(message)
    {
      case WM_CREATE:
        hDC = GetDC(hwnd);
        GetTextMetrics(hDC, &tm);
        xChar = tm.tmAveCharWidth;
        yChar = tm.tmHeight + tm.tmExternalLeading;
        ReleaseDC(hwnd, hDC);
        return 0L;

      case WM_PAINT:
        hDC = BeginPaint(hwnd, &ps);
        SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowExt(hdc, 1, 1);
        SetViewportExt(hdc, xChar, yChar);
        line = 0;
        if((fp = fopen("disptext.cpp", "r")) != NULL)
        {
          while(!feof(fp)) {
            int i = 0;
            while((ch = fgetc(fp)) != '/n' && ch != EOF)
              szBuffer[i++] = (char)ch;
            TextOut(hDC, xChar, line*yChar, szBuffer, i);
            line++;
          }
          fclose(fp);
        }
        EndPaint(hwnd, &ps);
        return 0L;

      case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }


3.5、設備信息
  一個設備對象通常與一個物理顯示設備(例如顯示器和打印機等)相關聯,這些設備的有關信息可以通過調用GetDeviceCaps函數來獲取。表3-8給出了該函數的一些使用說明,在本指南中,當需要用該函數的其他功能時,再進行有關的討論。

  表3-8 GetDeviceCaps 函數
用 途獲取一個設備的設備信息。原 型int GetDeviceCaps(   HDC hDC,設備對象句柄  int nIndex ); 返回值返回所需要項的值。
  表3-8-1 nIndex 值 常量說明HORZSIZE物理顯示器的寬度(mm)VERTSIZE物理顯示器的高度(mm)HORZRES物理顯示器的寬度(象素)VERTRES物理顯示器的高度(象素,嚴格說是光柵行數)LOGPIEXLSX沿顯示寬度方向每邏輯英寸的象素數LOGPIEXLSY沿顯示高度方向每邏輯英寸的象素數BITSPIEXL表示每個象素所需要的位數目PLANES色平面數NUMBRUSHES刷子的數目NUMPENS筆的數目NUMFONTS字體的數目NUMCOLORS設備的顏色表中的項目數aspECTX一個象素的相對寬度ASPECTY一個象素的相對高度ASPECTXY象素的相對對角線寬度
  LOGPIXELSX和LOGPIXELSY的值分別是水平和垂直方向每一“邏輯寸”中的象素數。一個邏輯寸大小約是1.5英寸,這取決于顯示設備的分辨率。邏輯寸實際上放大顯示,以便將所顯示的文本放大到一個適當的大小。

3.6、顏色的使用
  關于顏色的定量表示,有許多方法,其中一種方法分別使用0到255的數值來表示一種顏色中紅(R)、綠(G)和藍(B)三基色的相對強度,這中方法被稱為RGB顏色模型。Windows用一個無符號長整數來表示顏色,其中最低三字節分別指定0到255范圍內的紅、綠和藍三基色的相對強度。在Windows中,使用COLORREF類型(或DWORD類型)的數據表示RGB顏色值。
  在Windows.h中定義的宏RGB將三個表示紅、綠、藍的值組成一個RGB顏色值。例如:RGB(0,0,0)表示黑色,RGB(255,255,255)表示白色,其他常見的RGB顏色如下:

類型說明RGB(255, 0, 0)紅色RGB(0, 255, 0)綠色RGB(0, 0, 255)藍色RGB(255, 0, 255)紫色RGB(255, 255, 0)黃色RGB(0, 255, 255)青色
  宏GetRValue、GetGValue和GetBValue從RGB值中提取無符號單字節的基色值。
  Windows還可以通過灰度比方式來顯示附加的顏色,即將不同的單色象素組合成象素圖案,不同的紅、綠、藍顏色組成不同的灰度比圖案。
  另外,Windows為了對各個顯示元素進行著色,還維護著19種系統顏色(見表示表3-9),程序員可以使用函數GetSysColor或SetSysColors來獲取或設置這些系統顏色(見表3-10)。這些顏色的缺省值是由設備驅動程序提供的。

  表3-9 Windows的缺省系統顏色 常量說明COLOR_SCROLLBAR滾動杠的灰色區域COLOR_BACKGROUND桌面的背景(屏幕背景)COLOR_ACTIVECAPTION活動窗口的標題欄COLOR_INACTIVECAPTION非活動窗口的標題欄COLOR_MENU選單背景COLOR_MENUTEXT選單文本COLOR_WINDOW窗口的背景COLOR_WINDOWFRAME窗口的框架COLOR_CAPTIONTEXT標題欄中的文本COLOR_ACTIVEBORDER活動窗口的邊框COLOR_APPWORKSPACE多文檔界面應用程序的背景COLOR_HIGHLIGHT選項COLOR_HIGHLIGHTTEXT選項中的文本COLOR_BTNFACE按鈕的邊緣COLOR_BTNTEXT按鈕上的文本COLOR_GRAYTEXT按鈕(灰色文本)
  表3-10-1 SetSysColors 函數
用 途該函數為一個或多個顯示元素設置系統顏色(顯示元素指的是系統和窗口的各個不同的部分。原 型VOID SetSysColors(    int nChanges, 需改變的系統顏色數目。  LPINT lpSysColor, int類型的指針,所指向的變量中指定了將要改變的元素,可用的值見表3-9?! WORD FAR *lpColorValue 所指向的變量中包含了每個元素新的RGB顏色值。); 注 意該函數將改變當前正運行的所有應用程序的窗口顏色。該函數將各窗口發送WM_SYSCOLORCHANG消息,通知顏色的變化。
  表3-10-2 GetSysColor 函數
用 途檢索指定的顯示元素的當前色。原 型VOID GetSysColors(    int nIndex, 指定要檢索其顏色的顯示元素,見表3-9。); 返回值所指定的顯示元素的RGB值,對單色顯示器,系統顏色可解釋為灰度圖案。
3.7、使用刷子
  刷子用于擦除用戶區中顯示的內容,也用于填充GDI圖形函數建立的封閉圖形。Windows提供的可供程序員使用的庫存刷子有:

常量說明WHITE_BRUSH白色刷子LTGRAY_BRUSH淺灰色刷子GRAY_BRUSH灰色刷子DKGRAY_BRUSH深灰色刷子BLACK_BRUSH黑色刷子HOLLOW(or NULL)_BRUSH空刷子
  一個刷子由HBRUSH類型的句柄標識。庫存刷子的句柄可由函數GetStockObject函數獲得。例如,下面的語句獲得一個灰色刷子,并將其句柄存于hBrush中。

  HBRUSH hBrush = GetStockObject(GRAY_BRUSH);

  使用函數SelectObject將一個刷子選入一個設備對象中,這樣,在以后填充封閉圖形時,使用選入到設備對象中的刷子。例如,將上面的語句所獲得的庫存刷子選入hDC標識的設備對象中,使用語句:

  hOldBrush = SelectObject(hDC, hBrush);

  函數SelectObject返回設備對象中先前已存在的刷子,在上例中,將它保存在HBRUSH類型的變量hOldBrush中。程序員可以使用函數CreateSolidBrush和CreateHatchBrush創建自己的刷子,表3-11給出了這兩個函數的說明。

  表3-11-1 CreateSolidBrush 函數
用 途創建一個指定顏色的邏輯刷子。原 型HBRUSH CreateSolidBrush   COLORREF crColorRGB顏色值。); 注 意假如調用成功,返回一個刷子句柄,否則返回NULL。
  表3-11-2 CreateHatchBrush 函數
用 途創建一個具有指定陰影圖案和顏色的邏輯刷子。原 型HBRUSH CreateHatchBrush   int nIndex, 刷子的陰影類型,見后面的說明?! OLORREF crColor刷子影線的顏色。); 返回值假如調用成功,返回一個刷子句柄,否則返回NULL。
常量說明HS_HORIZONTAL- - - -  水平影線HS_VERTICAL   垂直影線HS_FDIAGONAL/ / / /  45度向上影線HS_BDIAGONAL/ / / /  45度向下影線HS_CROSS+ + + +  水平和垂直交叉影線HS_DIAGCROSSX X X X  45度交叉影線
  例如,下列程序段分別創建一個綠色刷子和一個紅色影線刷子。

  hGreenBrush = CreateSolidBrush(RGB(0,255,0));
  hRedHatchedBrush = CreateHatchBrush(HS_CROSS, RGB(255,0,0));

  程序員創建的刷子在不再使用時一定要刪除(使用函數DeleteObject)。刪除庫存刷子是非法的。

  DeleteObject(hGreenBrush);
  DeleteObject(hRedHatchedBrush);

  下面的程序建立一個背景色為交叉影線的窗口。

  LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    static HBRUSH OrgBrush, hBrush;
    HDC hDC;
    char msg[256] = "Hello, Welcome to cgd.gamedoor.net!";
    PAINTSTRUCT ps;

    switch(message)
    {
      case WM_CREATE:
        OrgBrush = GetClassWord(hwnd, GCW_HBRBACKGROUND);
        hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(255,255,0));
        SetClassWord(hwnd, GCW_HBRBACKGROUND, hBrush);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWidnow(hwnd);
        return 0;

      case WM_PAINT:
        hDC = BeginPaint(hwnd, &ps);
        SetBkMode(hDC, TRANSPARENT);
        TextOut(hDC, 0, 0, msg, sizeof(msg)-1);
        ValidateRect(hwnd, NULL);
        EndPaint(hwnd, &ps);
        return 0;

      case WM_DESTROY:
        SetClassWord(hwnd, GCW_HBRBACKGROUND, OrgBrush);
        DeleteObject(hBrush);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }

  在程序中,語句:

  GetClassWord(hwnd, GCW_HBRBACKGROUND);

  獲取在類所指定的刷子的句柄,語句:

  SetClassWord(hwnd, GCW_HBRBACKGROUND, hBrush);

  重新將類刷子設置為程序員所創建的刷子。為了用新刷子清楚用戶區,語句:

  InvalidateRect(hwnd, NULL, TRUE);

  使整個用戶區無效。程序中使用的函數SetBkMode見3.9節。

3.8、使用筆
  GDI函數使用筆畫線或繪制圖形的輪廓。Windows庫存的筆包括:

常量說明WHITE_PEN白色筆BLACK_PEN黑色筆NULL_PEN空筆
  筆有類型為HPEN的句柄來標識,庫存的筆可用函數GetStockObject來獲取:

  HPEN hPen = GetStockObject(WHITE_PEN);

  它返回標識一個庫存筆的句柄。設備對象中缺省的筆是BLACK_PEN。程序員也可以使用函數GreatePen創建定制的筆,如表3-12。

  表3-12-1 GreatePen 函數
用 途創建一支具有指定類型、寬度和顏色的邏輯筆。原 型HPEN CreatePen(    int nPenStyle,筆的類型,見后面的說明  int nWidth,以邏輯單位定義的筆的寬度  COLORREF crColor RGB顏色值) 返回值假如函數成功,則返回值是標識一個邏輯筆對象的句柄,否則為NULL。
  表3-12-2 GreatePenIndirect 函數
用 途創建一支邏輯筆。原 型HPEN CreatePenIndirect(    LOGPEN FAR *lpLogPen指向包含邏輯筆類型、寬度和顏色的一個LOGPEN類型的變量) 返回值返回一個邏輯筆對象的句柄,假如不成功,返回NULL。
  類型LOGPEN的說明為:

  typedef struct tagLOGPEN {
    WORD lopnStyle;   // 筆的類型,見后表的說明。
    POINT lopnWidth;   // 筆的寬度(邏輯單位),若要為0,則寬度為一個象素寬。
    COLORREF lopnColor; // 筆的顏色,為RGB顏色值。
  } LOGPEN;
     常量說明PS_SOLID_______PS_DASH-------PS_DOT.......PS_DASHDOT_._._._PS_DASHDOTDOT_.._.._.._PS_NULL空筆PS_INSIDEFRAME________
筆的類型由上列常量定義

  注重:假如筆的寬大于1,并且筆的類型為PS_INSIDEFRAME,則線條被畫在多邊形基本框架里邊;假如筆的寬度大小或等于1,則類型PS_INSIDEFRAME與類型PS_SOLIDE完全相同;假如筆的顏色不能與可利用的RGB值相匹配,則PS_INSIDEFRAME類型的筆的顏色使用邏輯顏色,它通過將幾種純顏色混合形成所需要的顏色。物理寬度大于1象素的筆總為空或實類型的筆。
  下面的程序片段使用函數CreatePen創建一支紅色筆,并選入到設備對象中:

  HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255,0,0));
  hPen = SelectObject(hDC, hPen);

  當程序創建的筆不再使用時,應使用函數DeletObject將其刪除:

  DeleteObject(hPen);

  但刪除庫存筆是非法的。庫存筆和程序員創建的任何寬度為0的筆的實際寬度為1個象素,這在高分辨率的顯示器上表示為極細的線。假如當前的映射方式是MM_TEXT方式,最好通過調用帶有SM_CXBORDER和SM_CYBORDER索引的GetSystemMetrics(見表3-13)來獲得單線窗口邊框的寬度,將這些值用作筆寬。也可使用其他映射方式為程序中創建的筆設置特定的物理寬度。

  表3-13 GetSystemMetrics 函數
用 途檢索系統度量。系統度量是Windows顯示的各種元素的寬和高。原 型int GetSystemMetrics(    int nIndex指定要檢索的系統度量,見后面的說明) 返回值返回所要求的以象素給出的系統度量。
常量說明SM_CXSCREEN屏幕的寬度SM_CYSCREEN屏幕的高度SM_CXFRAME能縮放窗口的邊框的寬度SM_CYFRAME能縮放窗口的邊框的高度SM_CXVSCROLL垂直滾動杠箭頭的位圖的寬度SM_CYVSCROLL垂直滾動杠箭頭的位圖的高度SM_CXHSCROLL水平滾動杠箭頭的位圖的寬度SM_CYHSCROLL水平滾動杠箭頭的位圖的高度SM_CXBORDER不能縮放的窗口的邊框的寬度SM_CYBORDER不能縮放的窗口的邊框的高度SM_CXDLGFRAME具有WS_DLGFRAME窗口的邊框的寬度SM_CYDLGFRAME具有WS_DLGFRAME窗口的邊框的高度SM_CXICON圖標的寬度SM_CYICON圖標的高度SM_CXCURSOR光標的寬度SM_CYCURSOR光標的高度SM_CXFULLSCREEN全屏幕窗口的窗口區寬度SM_CYFULLSCREEN全屏幕窗口的窗口區高度

 3.9、填充空隙
  在點劃線、短劃線筆或影線刷子的使用中,點和短劃線之間的空隙以及刷子中的陰影間隙的著色取決于設備對象中定義的背景方式和背景顏色。缺省的背景方式是OPAQUE,即Windows用背景色填充空隙,缺省的背景顏色是白色,即與大多數程序在窗口類別中用作清除窗口背景的WHITE_BRUSH型刷子相一致??梢酝ㄟ^調用函數SetBkColor設背景色,使用函數GetBkColor獲得設備對象中定義的當前背景色(見表3-14)。

  表3-14-1 SetBkColor 函數
用 途設置當前的背景色。原 型DWORD SetBkColor(    HDC hDC,設備對象句柄  COLORREF crColor 新的背景色,RG顏色值) 返回值返回作為一種顏色值的當前的背景色。若小于0,則出錯。
  注釋:假如背景方式為OPAQUE,那么GDI就用背景色填充設計的行距、筆、刷子中陰影間隙,以及各字符中的間隙。假如設備不能表示由crColor參數指定的RGB顏色值,此函數將當前背景色設置為最接近的物理色(純顏色),并返回之。

  表3-14-2 GetBkColor 函數
用 途返回指定設備的當前背景色。原 型DWORD GetBkColor(    HDC hDC設備對象句柄) 返回值當前背景色的RGB顏色值。
  當前背景方式決定Windows是否填充空隙。函數SetBkMode及GetBkMode用于設置和獲取背景方式,如表3-15。

  表3-15-1 SetBkMode 函數
用 途設置與文本和線型一起使用的背景方式。背景方式決定在顯示文本、刷子或非實線的普通筆型之前,GDI是否要將設備顯示表面已有顯示內容用背景色清除。原 型int SetBkMode(    HDC hDC設備對象句柄  int nBkMode背景方式。見后面說明) 返回值返回OPAQUE或TRANSPARENT,以說明先前的背景方式。
常量說明OPAQUE在文本、刷子或筆繪圖之前,用當前背景色填充設備顯示表面TRANSPARENT背景保留不變
  表3-15-2 GetBkMode 函數
用 途返回設備的背景方式。原 型int GetBkMode(    HDC hDC設備對象句柄) 返回值返回當前背景方式(OPAQUE或TRANSPARENT)。
3.10、設置文本屬性
  有一些設備對象屬性影響文本。缺省時設備對象所繪制的文本顏色為黑色??梢允褂煤瘮礢etTextColor或函數GetTextColor設置或獲取設備對象中的文本顏色,如表3-17。

  表3-16-1 SetTextColor 函數
用 途設置文本顏色。原 型DWORD SetTextColor(    HDC hDC設備對象句柄  COLORREF crColor 指定的文本顏色(RGB值)) 返回值返回先前的文本顏色(RGB值)。
  注釋:假如設備不支持指定的顏色,則設置成最接近的物理顏色。

  表3-16-2 GetTextColor 函數
用 途設置當前的文本顏色。原 型DWORD GetTextColor(    HDC hDC設備對象句柄) 返回值當前使用的文本顏色(RGB值)。
  下面的程序是對3.4及3.7節的程序修改后所形成的一個示例程序,這個程序演示了在程序中設置文本屬性方法。

  // 3-10.c
  #include <stdio.h>
  #include <windows.h>

  LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

  int PASCAL WinMain(
    HINSTANCE hInstance,   // 應用程序的實例句柄
    HINSTANCE hPrevInstance, // 該應用程序前一個實例的句柄
    LPSTR lpszCmdLine,    // 命令行參數串
    int nCmdShow )      // 程序在初始化時如何顯示窗口
  {
    ... ...
  }

  LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    static HBRUSH OrgBrush, hBrush;
    static int xChar, yChar;
    int line;
    char szBuffer[256];
    HDC hDC;
    char ch;
    PAINTSTRUCT ps;
    TEXTMETRIC tm;
    FILE *fp;

    switch(message)
    {
      case WM_CREATE:
        OrgBrush = GetClassWord(hwnd, GCW_HBRBACKGROUND);
        hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(255, 255, 0)
        SetClassWord(hwnd, GCW_HBRBACKGROUND, hBrush);
        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        
        hDC = GetDC(hwnd);
        GetTextMetrics(hDC, &tm);
        xChar = tm.tmAveCharWidth;
        yChar = tm.tmHeight + tm.tmExternalLeading;
        ReleaseDC(hwnd, hDC);
        return 0;

      case WM_PAINT:
        hDC = BeginPaint(hwnd, &ps);
        SetBkMode(hDC, OPAQUE);
        SetTextColor(hDC, RGB(255, 0, 0));
        SetBkColor(hDC, RGB(0, 255, 0));

        SetMapMode(hdc, MM_ANISOTROPIC);
        SetWindowExt(hdc, 1, 1);
        SetViewportExt(hdc, xChar, yChar);

        line = 0;
        if((fp = fopen("3-10.c", "r")) != NULL)
        {
          while(!feof(fp)) {
            int i = 0;
            while((ch = fgetc(fp)) != '/n' && ch != EOF)
              szBuffer[i++] = (char)ch;
            TextOut(hDC, xChar, line*yChar, szBuffer, i);
            line++;
          }
          fclose(fp);
        }
        EndPaint(hwnd, &ps);
        return 0L;

      case WM_DESTROY:
        SetClassWord(hwnd, GCW_HBRBACKGROUND, OrgBrush);
        DeleteObject(hBrush);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }

  修改這個程序,將背景方式設置為TRANSPARENT,觀察程序的運行結果。

3.11、公用和私用顯示設備
  在前面,我們曾介紹說,Windows維護著五個公用的顯示設備對象,當窗口對象需要繪制其用戶區時,使用函數GetDC()或BeginPaint()從Windows那里借用。一般而言,函數GetDC()和BeginPaint()所借用來的公用顯示設備對象的初始屬性為表3-1中所給出的缺省屬性,窗口對象可以使用有關的函數改變這些屬性,以滿足其繪制要求。在繪制工作完成以后,窗口對象應將顯示設備對象歸還給Windows。但注重,當調用ReleaseDC()或EndPaint()函數將公用顯示設備對象歸還Windows后,對顯示設備對象的屬性所做的改變也就丟失了。這樣,當窗口對象再次借用顯示設備對象時,顯示設備對象的初始屬性仍為缺省屬性,窗口對象在每次使用非缺省屬性進行繪制之前,都必須使用有關的函數設置顯示設備對象的屬性。
  另外的一種情況是當窗口對象在一段程序中改變了一些設備對象屬性,在使用改變后的設備對象屬性工作之后,窗口對象又要使用原先的設備對象屬性繼續工作,對這種情況,一種比較方便的方法是通過使用函數SaveDC()和RestoreDC來簡化對設備對象的治理,這兩個函數的使用說明見表3-17。

  表3-16-1 SaveDC 函數

用 途該函數用于保存其參數所標識的設備對象的當前屬性信息,它通過將這些屬性信息拷貝到一個設備對象環境棧中來實現。原 型int SaveDC(    HDC hDC設備對象句柄) 返回值返回一個序數值來反映被保存的設備對象,假如該函數在執行中出錯,則返回0。
  注釋:該函數可被調用多次來分別保存多個設備對象的多個屬性信息。

  表3-16-2 RestoreDC 函數
用 途恢復設備對象的屬性信息。原 型BOOL RestoreDC(    HDC hDC設備對象句柄  int nSaveDC指定要被恢復的屬性信息,可以是函數SaveDC()返回的值或為-1,若為-1,則設備對象被恢復為設備對象環境棧頂所保存的屬性信息。) 返回值非零表示恢復成功,零表示失敗。
  注釋:該函數通過拷貝設備對象環境棧中保存的屬性信息來恢復指定的設備對象屬性。假如由參數nSaveDC指定的設備對象信息不處于環境棧的棧頂,那以函數RestoreDC()會不斷地刪除棧頂信息,直到找到由參數nSaveDC所指定的設備對象信息為止,這些被刪除的信息永遠被丟失了。
  下面的程序片段用于演示函數SaveDC()和RestoreDC()使用方法:

  // ......
  int nSaveID = SaveDC(hDC);
  SetBkMode(hDC, OPAQUE);
  SetTextColor(hDC, RGB(255, 0, 0));
  SetBkColor(hDC, RGB(0, 255, 0));

  SetMapMode(hDC, MM_ANISOTROPIC);
  SetWindowsExt(hDC, 1, 1);
  SetViewportExt(hDC, xChar, yChar);
  // ......
  RestoreDC(nSaveID);
  // ......

  使用公用顯示設備對象的優點是,這些顯示設備對象為系統中的所有應用程序共享。但不足之處在于存在與借用、設置和歸還顯示設備對象有關的許多工作,對一些復雜的繪圖程序,或使用非缺省屬性的應用程序,使用公用顯示設備對象會增加程序的編碼量,并有可能影響到應用程序的效率。對這些情況,程序員可以為一個或多個窗口對象指定使用私用顯示設備對象,當在注冊窗口類時,為該窗口類指定CS_OWNDC風格參數,這時,由這種窗口類所創建的窗口對象都擁有自己的私用顯示設備對象。
  使用私用顯示設備對象的優點在于,由于私用的顯示設備對象僅供一個窗口對象專用,這樣,窗口對象在一條消息處理完備以后從窗口函數返回之前也不需要歸還顯示設備對象。窗口對象可以將顯示設備對象的句柄保存在一個靜態生存期的變量中,供自己使用。例如,窗口對象在處理WM_CREATE消息時設置設備的屬性,而在處理WM_PAINT或其他消息時直接使用所設置對象屬性。

  // 3-11.c
  #include <stdio.h>
  #include <windows.h>

  LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

  int PASCAL WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine,
    int nCmdShow)
  {
     char szAppName[] = "DispText";
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;
    HBRUSH hBrush;
    HDC hDC;
    int xChar, yChar;
    TEXTMETRIC tm;

    hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(255, 255, 0));
    if (!hPrevInstance) {
      wndclass.style = CS_VREDRAW CS_HREDRAW CS_OWNDC;
      wndclass.lpfnWndProc = WndProc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hInstance = hInstance;
      wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
      wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndclass.hbrBackground = hBrush;
      wndclass.lpszMenuName = NULL;
      wndclass.lpszClassName = szAppName;

      if (!RegisterClass(&wndclass))
        return FALSE;
    }

    hwnd = CreateWindow(
      szAppName,
      "Display Text",
      WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, CW_USEDEFAULT,
      CW_USEDEFAULT, CW_USEDEFAULT,
      NULL,
      NULL,
      hInstance,
      NULL );

    hDC = GetDC(hwnd);
    GetTextMetricse(hDC, &tm);
    xChar = tm.tmAveCharWidth;
    yChar = tm.tmHeight + tm.tmExternalLeading;

    SetBkMode(hDC, OPAQUE):
    SetTextColor(hDC, RGB(255, 0, 0));
    SetBkColor(hDC, RGB(0, 255, 0));
    
    SetMapMode(hDC, MM_ANISOTROPIC);
    SetWindowExt(hDC, 1, 1)
    SetViewportExt(hDC, xChar, yChar);
    
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while( GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    ReleaseDC(hwnd, hDC);
    DeleteObject(hBrush);

    return msg.wParam;
  }

  LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
    int line;
    char szBuffer[256];
    HDC hDC;
    char ch;
    PAINTSTRUCT ps;
    FILE *fp;

    switch(message)
    {
      case WM_PAINT:
        hDC = BeginPaint(hwnd, &ps);

        line = 0;
        if((fp = fopen("3-11.c", "r")) != NULL)
        {
          while(!feof(fp)) {
            int i = 0;
            while((ch = fgetc(fp)) != '/n' && ch != EOF)
              szBuffer[i++] = (char)ch;
            TextOut(hDC, xChar, line*yChar, szBuffer, i);
            line++;
          }
          fclose(fp);
        }
        EndPaint(hwnd, &ps);
        return 0;

      case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
  }

  在一個窗口對象使用私用顯示設備對象的情況下,函數GetDC()和BeginPaint()返回的句柄標識的設備對象是窗口的私用顯示設備對象。從這個程序還可以看出,私用的顯示設備對象保持當前設置的設備屬性,對私用的顯示設備對象,不存在“借用”和“歸還”之說。但使用CS_OWNDC只影響到從GetDC和BeginPaint檢索到的設備對象,而不會影響到使用別的函數(例如GetWindowDC()等函數)檢索到的設備對象。
  一個私用顯示設備對象大約占用200字節,所有的字節都從GDI模塊的局部堆中分配,而這個堆由系統中當前正運行的所有應用程序共享。假如系統中的所有應用程序都使用私用顯示設備對象,那將會用完GDI的局部堆,導致系統崩潰。因此,除非必要,應用程序一般不使用私用顯示設備對象。
  介于使用公用和私用顯示設備對象之間的是使用類顯示設備對象。若在注冊窗口類時指定了CS_CLASSDC風格參數,則該窗口類擁有一個可供該類所有窗口對象(包括在該應用程序的其它實例中所創建的窗口對象)共用的顯示設備對象。這種類型的顯示設備對象被稱為類顯示設備對象。類顯示設備對象由于被該類的所有窗口對象共享,因此,一個窗口對象在使用類顯示設備對象時需要從類中借用,并在使用完之后歸還給類,否則,該類的其它對象就無法使用類顯示設備對象。但有一點應引起注重,類顯示設備對象同私用顯示設備對象一樣,也保持當前設置的屬性,即使在歸還類的情況下也是如此。這就是說,假如該類的一個窗口對象改變了該顯示設備對象的屬性,那么,也要影響到基于該窗口類所創建的所有窗口對象(包括在該應用程序的其它實例中所創建的窗口對象)。因此,類顯示設備對象較難使用,但它相對于私用顯示設備對象而言,可以節省一些內存空間。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲成人网久久久| 欧美激情中文字幕乱码免费| 亚洲最大成人免费视频| 中文字幕亚洲欧美日韩在线不卡| 伊人久久久久久久久久久久久| www.国产精品一二区| 国产一区二区精品丝袜| 久久手机免费视频| 久久久久久久久爱| 亚洲欧美中文字幕在线一区| 久久精品成人欧美大片| 亚洲乱码av中文一区二区| 日韩精品视频在线| 在线免费观看羞羞视频一区二区| 欧美综合在线观看| 国产mv久久久| 日韩电影免费观看中文字幕| 国模视频一区二区| 日韩不卡中文字幕| 北条麻妃久久精品| 亚洲影视九九影院在线观看| 欧美日韩视频在线| 国产精品第一页在线| 久久精品亚洲94久久精品| 亚洲精品一区中文| 性视频1819p久久| 最近2019好看的中文字幕免费| 久久久之久亚州精品露出| 亚洲精品av在线播放| 日韩中文av在线| 中文字幕亚洲一区二区三区| 日韩亚洲欧美中文在线| 亚洲伊人久久大香线蕉av| 久久天天躁狠狠躁夜夜躁2014| 亚洲在线观看视频| 欧美日韩亚洲视频一区| 91av在线国产| 欧美丝袜一区二区三区| 欧美日韩国产成人高清视频| 国产精品久久久久久av福利| 亚洲已满18点击进入在线看片| 欧美理论在线观看| 亚洲在线免费看| 欧洲中文字幕国产精品| 日韩第一页在线| 国产欧美日韩免费| 欧洲s码亚洲m码精品一区| 国产成人高清激情视频在线观看| 成人在线中文字幕| 欧美二区在线播放| 欧美午夜丰满在线18影院| 亚洲第一精品电影| 社区色欧美激情 | 成人在线观看视频网站| 国产精品福利网| 欧美在线影院在线视频| 亚洲综合在线做性| 成人性生交大片免费看小说| 欧美在线免费观看| 日韩成人中文字幕| 欧美伊久线香蕉线新在线| 亚洲精品久久久久久久久久久| 亚洲高清不卡av| 国产精品久久一区| 久久久久久久久中文字幕| 国产男女猛烈无遮挡91| 国产一区二区三区三区在线观看| 亚洲精品欧美日韩专区| 亚洲第一区第一页| 欧美激情精品久久久久久大尺度| 91爱视频在线| 色一情一乱一区二区| 久久亚洲电影天堂| 欧美俄罗斯乱妇| 伊是香蕉大人久久| 亚洲国产小视频在线观看| 国产在线精品成人一区二区三区| 亚洲欧美精品伊人久久| 亚洲精品99久久久久| 2019国产精品自在线拍国产不卡| 久久艳片www.17c.com| 欧美激情国内偷拍| 亚洲成人av在线播放| 国产日韩欧美电影在线观看| 性夜试看影院91社区| 亚洲女人天堂网| 久久视频在线免费观看| 欧美性视频在线| 亚洲国产私拍精品国模在线观看| 亚洲精品国产综合区久久久久久久| 97色在线播放视频| 北条麻妃99精品青青久久| 色噜噜狠狠狠综合曰曰曰88av| 91成人国产在线观看| 在线精品视频视频中文字幕| 亚洲国产又黄又爽女人高潮的| 国产精品国内视频| 国产精品aaaa| 7777kkkk成人观看| 色综久久综合桃花网| zzjj国产精品一区二区| 在线观看国产成人av片| 欧美xxxx18性欧美| 国产精品国产福利国产秒拍| 久久精品国产一区二区三区| 欧美日韩一区二区免费在线观看| 2019亚洲日韩新视频| 精品国产自在精品国产浪潮| 一区二区三区在线播放欧美| 国产日韩欧美电影在线观看| 欧美日韩国产123| 亚洲一区二区三区香蕉| 亚洲电影免费观看高清完整版在线观看| 九九久久久久久久久激情| 国产精品久久久久久久久久久久久| 欧美精品一区二区三区国产精品| 国产69精品久久久久久| 亚洲天天在线日亚洲洲精| 亚洲男人天堂2023| 国产精品永久在线| 国产精品男人爽免费视频1| 欧美日韩另类字幕中文| 欧美最顶级丰满的aⅴ艳星| 精品国内产的精品视频在线观看| 亚洲成人av片在线观看| 欧美日韩国产限制| 亚洲激情视频在线播放| 在线观看日韩专区| 久久视频免费在线播放| 黄色一区二区三区| 欧美精品一区三区| 韩国日本不卡在线| 超碰精品一区二区三区乱码| 精品自拍视频在线观看| 欧美午夜性色大片在线观看| 国内精久久久久久久久久人| 97视频在线观看网址| 国产中文日韩欧美| 黄色一区二区在线观看| 中文.日本.精品| 美日韩丰满少妇在线观看| 国产精品久久久久久久av大片| 一本色道久久综合狠狠躁篇的优点| 国产一区二区三区在线观看网站| 亚洲一区二区三区视频| 久久在线免费观看视频| 亚洲久久久久久久久久久| 91性高湖久久久久久久久_久久99| 日韩中文字幕不卡视频| 亚洲国产精品久久91精品| 国产成人在线一区二区| 欧美一级黄色网| 亚洲精品一区二区网址| 欧美成人激情视频| 久久九九全国免费精品观看| 国产免费一区二区三区香蕉精| 综合国产在线观看| 18一19gay欧美视频网站| 亚洲精品视频在线观看视频| 欧美激情a∨在线视频播放| 国产中文欧美精品| 欧美成人精品影院| 亚洲二区中文字幕| 富二代精品短视频|