设备描述表还真是十分重要,以至于我们在这里还要再学习它。这节内容比较少,相信你很快就应该能看完。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消息,且一直发送 下去。