分享

Windows API

 fisher60 2013-02-04

Windows API-GDI入门基础知识详解

什么是GDI?

  GDI是Graphics Device Interface的缩写,含义是图形设备接口,它的主要任务是负责系统与绘图程序之间的信息交换,处理所有Windows程序的图形输出。

  在Windows操作系统下,绝大多数具备图形界面的应用程序都离不开GDI,我们利用GDI所提供的众多函数就可以方便的在屏幕、打印机及其它输出设备上输出图形,文本等操作。GDI的出现使程序员无需要关心硬件设备及设备驱动,就可以将应用程序的输出转化为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。

GDI是如何实现输出的?

  要想在屏幕或者其它输出设备上输出图形或者文字,那么我们就必须先获得一个称为设备描述表( DC:Device Context)的对象的句柄,以它为参数,调用各种GDI函数实现各种文字或图形的输出。

  设备描述表是GDI内部保存数据的一种数据结构,此结构中的属性内容与特定的输出设备(显示器,打印机等)相关,属性定义了GDI函数的工作细节,在稍后我们将看到如何使用TextOut函数输出文字,在这里属性确定了文字的颜色,x坐标和y坐标映射到窗口显示区域的方式等。

  设备描述表句柄一旦获得,那么系统将使用默认的属性值填充设备描述表结构。

  如果有必要,我们可以使用一些GDI函数获取和改变设备描述表中的属性值。

什么是有效矩形什么是无效矩形?

  当应用程序接受到WM_PAINT消息后通常就准备更新正个显示区域,但是通常只需要更新一个比较小的区域而不是整个区域,这种情况通常出现在当应用程序的主窗口的一部分被一个对话框覆盖,需要重画的只是被覆盖的矩形区域(见下图)。

  EXE示例程序下载:点击这里下载(90K, winzip压缩文件)

  阴影以下的部分就是需要更新的矩形区域,该区域就是我们所说的无效区域,正是因为此区域的存在,系统才会向消息队列中放入一个WM_PAINT消息。

  Windows内部为每个窗口都保留了一个绘图结构(PAINTSTRUCT),它包含了包围无效区域的最小矩形的坐标和一些其它信息,需要注意的是当窗口消息处理函数在处理WM_PAINT消息之前显示区域中出现了另一个无效区域,那么Windows会计算出一个包围两个无效区域的新无效区域,并把这种变化保存在绘图结构(PAINTSTRUCT)中,Windows是不会同时把多个WM_PAINT消息同时放到消息队列中的。

  窗口消息处理函数是通过调用InvalidateRect函数使窗口显示区域内的矩形变为无效的,如果消息队列中已经存在一个WM_PAINT消息,那么Windows将计算出新的无效矩形,在接收到WM_PAINT消息的时候,窗口消息处理函数可以获得无效矩形的座标,通过调用GetUpdateRect,可以在任何时候获得这些坐标。

如何获取或释放设备描述表句柄?

   当应用程序需要绘图的时候,必须先获取设备描述表句柄,绘图操作结束后必须释放设备描述表句柄。我们有两种方法获取和释放设备描述表句柄。

1. 使用BeginPaint和Endpaint函数

  通常是在应用程序接收到WM_PAINT消息,也就是需要更新窗口的显示区域的时候调用BeginPaint函数获取设备描述表句柄的,使用完后调用Endpaint函数释放设备描述表句柄。

他们的函数原型为:

HDC BeginPaint(
HWND hwnd,,             // handle to window
LPPAINTSTRUCT pPaint   // paint information
);


BOOL EndPaint(
  HWND hWnd,                   // handle to window
  CONST PAINTSTRUCT *pPaint   // paint data
);

  从上面BeginPaint函数的原形中我们可以看到需要一个PAINTSTRUCT结构对象的内存地址,PAINTSTRUCT结构包含在WinUser.h头文件中。

  定义如下:

typedef struct tagPAINTSTRUCT {
  HDC         hdc; //设备描述表句柄
  BOOL        fErase; //擦除状态
  RECT        rcPaint; //无效矩形座标
  BOOL        fRestore;
  BOOL        fIncUpdate;
  BYTE        rgbReserved[32];
}
PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;

  事实上当程序调用BeginPaint函数的时候,Windows会自动的填写此结构的各个属性,而程序作者只需要关心前三个属性。

  第一个属性hdc表示当前的设备描述表句柄。

  第二个属性fErase来说,多数情况下它是被标记成FALSE(0)的,这表示Windows已经擦除了无效矩形的背景,这个擦除动作是是在BeginPaint函数中发生的,而擦除背景用的画刷则是WNDCLASS结构中的hbrBackground属性指定的画刷来擦除背景的,在很多情况下可能程序作者想自己定义一些插除行为,那么可以通过响应消息队列中的WM_ERASEBKGND消息来完成。

  第三个属性rcPaint则表示无效矩形座标,它定义了无效矩形的边界。

  RECT结构可以在WinDef.h头文件中找到。

  定义如下:

typedef struct tagRECT
{
LONG    left;
LONG    top;
LONG    right;
LONG    bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;

  注意,我们在前面提到了InvalidateRect函数,并已经清楚了调用它可以让窗口显示区域内的矩形变为无效,那么我们就可以在处理WM_PAINT消息的时候通过调用它实现在无效矩形外绘图,该调用是在调用BegingPaint函数之前调用的。

  使用方法:

InvalidateRect(hwnd,NULL,TRUE);

  通过上面代码的调用我们让整个显示区域变为了无效,并擦除背景,要注意的是,最后一个参数如果为FALSE,则不擦除背景,原有的东西将保留在原处,这通常是在接受到WM_PAINT消息的时候而不考虑rcPaint属性的情况下简单的重绘整个显示区域最方便的方法,例如,在显示区域内我们输出了一个图形,这个图形的一小部分落在了无效矩形区域内,而这就让绘制这个图形的无效部分变的没有意义,这时就需要重绘整个图形,因为在调用BeginPaint函数传回设备描述表句柄的时候,Windows不会绘制rcPaint也就是无效矩形以外的任何部分。

  对于InvalidateRect函数的详细举例,我们将在以后的章节中看到。

2. 使用GetDC和ReleaseDC函数

  在很多情况下我们可能需要在接收到非WM_PAINT消息的时候获取设备描述表句柄,通过调用GetDC函数我们可以获得设备描述表句柄,因为程序作者可能要使用设备描述表句柄完成其它工作,例如获得设备描述表属性,或者修改设备描述表属性值等,在最后我们与第一种方法一样要释放句柄,通过调用ReleaseDC函数完成工作。

  他们的函数原型为:

HDC GetDC(
  HWND hWnd    // handle to window
);


int ReleaseDC(
        HWND hWnd,  // handle to window
        HDC hDC      // handle to DC
);

  两种方法的区别:

  <1> 使用BeginPaint函数获得的的操作区域是显示区域中的无效矩形区域,接下来绘图操作只能在窗口的无效区域范围内进行,无效区域以外的区域将被忽略不能进行操作,而GetDC函数获得的操作区域则是整个窗口的显示区域,之后的操作可以在任何部分进行,而不只限制在无效区域。

  <2> BeginPaint函数会自动把无效区域变成有效的区域,而GetDC函数则不会将任何无效区域变得有效,必须强行调用ValidateRect函数,并把第二个参数设置为NULL来完成。

  最后我们给出一个可执行程序的例子,当应用程序执行的时候会有一个对话框出现,当你拉动这个对话框的时候又会出现同样的另一个对话框,这就证明了窗口的覆盖会造成无效矩形的出现,系统将会发送WM_PAINT消息。

  现在我们对GDI的基础概念有了一定的了解,下面我们将对GDI一些常用的函数进行学习。

TextOut函数

  TextOut函数的作用是把指定的字符串输出在我们指定的屏幕位置上。

函数原型:

 BOOL TextOut(
      HDC hdc,                    // 设备描述表句柄
      int nXStart,                  // 输出的x轴水平位置
      int nYStart,                  // 输出的y轴垂直位置
      LPCTSTR lpString,   // 指向要输出字符串的长指针
      int cbString                // 字符串长度
    );

  第一参数是设备描述表句柄,它既可以是通过BeginPaint函数获得的,也可以是通过GetDC函数获得的,需要提一下的是,设备描述表中的属性控制了显示的字符串的一些细节特征,比如字体、字体颜色、文字背景等,但要注意的是保存在设备描述表属性中的文字背景颜色和WNDCLASS结构中的屏幕背景是有区别的,文字背景指的是紧靠字符周围的矩形空间,又叫做字符框。而窗口背景则是一个画刷,Windows用它来擦除显示区域,它不是设备描述表结构中的一部分。

  第二和第三个参数定义了显示区域内字符串的开始位置,x是水平位置,y是垂直位置,字符串第一个字符位于坐标点(x,y),在设备描述表属性中,原点(x,y)均为为0,是显示区域的左上角,对于坐标来说,坐标的映射方式的不同决定了单位的不同,在通常情况下传递给函数的坐标被称为逻辑坐标,Windows有许多坐标映射方式,它们是用来控制GDI函数指定的逻辑坐标转换为显示器的实际像素坐标的方式。映射方式在设备描述表的属性中定义,默认的映射方式是MM_TEXT,我们可以在WinGdi.h头文件中找到。在MM_TEXT映射模式下,逻辑单位于实际单位都相同,都是像素,对于坐标来说,x的值从左向右递增,y的值则从上向下递增(见下图),MM_TEXT坐标系与Windows在PAINTSTURCT结构中定义的无效矩形所使用的坐标系相同。

  第四个参数是指向要输出字符串的长指针。

  第五个参数是要输出的字符串的实际长度。

  在Windows下输出文字并不如我们所想象的那么容易,在前面我们已经知道输出文字和坐标有关,我们为了精确的输出文字,就必须对系统的字体和字符大小有进一步的认识。

系统字体

  对于输出字符串的函数TextOut来说,在默认情况下设备描述表属性中使用的是系统字体(SYSTEM_FONT),系统字体是Windows用来在标题栏,功能表和对话框中显示字符串所使用的默认字体。

  但值得注意的是,字体如果按宽度来区分的话,大致可以分为两类:

  1. 等宽字体
  2. 变宽字体

  等宽字体意味着所有的字符宽度都是一致的,但随着计算技术的不断发展和推广,这种等宽字体就不再能够满足需要,于是变宽字体出现了,变宽字体不同的字符宽度都不一定相同。

  系统字体是一种点阵字体,字体被定义成了一个个的像素点,字体的确切大小取决于显示器的大小(分辨率的大小)。 

字符大小

  如果要使用TextOut函数显示多行文字,那么就必须确定字体字符的大小,字体的高度确定了下一行字符的显示位置,字体的宽度确定了下一列的显示位置。

  屏幕的分辨率和字符大小是确定如何显示字符的主要依据,为了获得当前系统上各种与视觉属性相关的信息,我们可以调用GetSystemMetrics函数获取,调用GetTextMetrics函数可以获取字体大小。

  以下是这两个函数的原形以及参数的详细定义:

int GetSystemMetrics(
  
  int nIndex  // 索引
);

  GetSystemMetrics函数是完成Windows图形输出的重要函数,它返回Windows中各种与视觉属性相关的信息,该函数需要只需要一个参数,它是一个索引,这些索引是在Windows头文件中定义的一些常量,这些常量分别指定了不同的与视觉相关的设备属性,这些索引的多少取决与Windows的版本。

  以下是这些索引的含义:

Value Meaning
SM_ARRANGE Flags specifying how the system arranged minimized windows. For more information about minimized windows, see the following Remarks section.
SM_CLEANBOOT Value that specifies how the system was started: 0 Normal boot
1 Fail-safe boot
2 Fail-safe with network boot
Fail-safe boot (also called SafeBoot, Safe Mode, or Clean Boot) bypasses the user's startup files.
SM_CMONITORS Number of display monitors on the desktop. See Remarks for more information. Windows NT, Windows 95:  This value is not supported.
SM_CMOUSEBUTTONS Number of buttons on mouse, or zero if no mouse is installed.
SM_CXBORDER, SM_CYBORDER Width and height of a window border, in pixels. This is equivalent to the SM_CXEDGE value for windows with the 3-D look.
SM_CXCURSOR, SM_CYCURSOR Width and height of a cursor, in pixels. The system cannot create cursors of other sizes.
SM_CXDLGFRAME, SM_CYDLGFRAME Same as SM_CXFIXEDFRAME and SM_CYFIXEDFRAME.
SM_CXDOUBLECLK, SM_CYDOUBLECLK Width and height of the rectangle around the location of a first click in a double-click sequence, in pixels. The second click must occur within this rectangle for the system to consider the two clicks a double-click. (The two clicks must also occur within a specified time.) To set the width and height of the double-click rectangle, call SystemParametersInfo with the SPI_SETDOUBLECLKHEIGHT and SPI_SETDOUBLECLKWIDTH flags.
SM_CXDRAG, SM_CYDRAG Width and height of a rectangle centered on a drag point to allow for limited movement of the mouse pointer before a drag operation begins. These values are in pixels. It allows the user to click and release the mouse button easily without unintentionally starting a drag operation.
SM_CXEDGE, SM_CYEDGE Dimensions of a 3-D border, in pixels. These are the 3-D counterparts of SM_CXBORDER and SM_CYBORDER.
SM_CXFIXEDFRAME, SM_CYFIXEDFRAME Thickness of the frame around the perimeter of a window that has a caption but is not sizable, in pixels. SM_CXFIXEDFRAME is the height of the horizontal border and SM_CYFIXEDFRAME is the width of the vertical border. Same as SM_CXDLGFRAME and SM_CYDLGFRAME.
SM_CXFOCUSBORDER, SM_CYFOCUSBORDER Width of the left and right edges and the height of the top and bottom edges of the focus rectangle drawn by DrawFocusRect. These values are in pixels. Windows 2000/NT, Windows Me/98/95:  This value is not supported.
SM_CXFRAME, SM_CYFRAME Same as SM_CXSIZEFRAME and SM_CYSIZEFRAME.
SM_CXFULLSCREEN, SM_CYFULLSCREEN Width and height of the client area for a full-screen window on the primary display monitor, in pixels. To get the coordinates of the portion of the screen not obscured by the system taskbar or by application desktop toolbars, call the SystemParametersInfo function with the SPI_GETWORKAREA value.
SM_CXHSCROLL, SM_CYHSCROLL Width of the arrow bitmap on a horizontal scroll bar, in pixels; and height of a horizontal scroll bar, in pixels.
SM_CXHTHUMB Width of the thumb box in a horizontal scroll bar, in pixels.
SM_CXICON, SM_CYICON Default width and height of an icon, in pixels. The LoadIcon function can load only icons of these dimensions.
SM_CXICONSPACING, SM_CYICONSPACING Dimensions of a grid cell for items in large icon view, in pixels. Each item fits into a rectangle of this size when arranged. These values are always greater than or equal to SM_CXICON and SM_CYICON.
SM_CXMAXIMIZED, SM_CYMAXIMIZED Default dimensions, in pixels, of a maximized top-level window on the primary display monitor.
SM_CXMAXTRACK, SM_CYMAXTRACK Default maximum dimensions of a window that has a caption and sizing borders, in pixels. This metric refers to the entire desktop. The user cannot drag the window frame to a size larger than these dimensions. A window can override these values by processing the WM_GETMINMAXINFO message.
SM_CXMENUCHECK, SM_CYMENUCHECK Dimensions of the default menu check-mark bitmap, in pixels.
SM_CXMENUSIZE, SM_CYMENUSIZE Dimensions of menu bar buttons, such as the child window close button used in the multiple document interface, in pixels.
SM_CXMIN, SM_CYMIN Minimum width and height of a window, in pixels.
SM_CXMINIMIZED, SM_CYMINIMIZED Dimensions of a minimized window, in pixels.
SM_CXMINSPACING SM_CYMINSPACING Dimensions of a grid cell for a minimized window, in pixels. Each minimized window fits into a rectangle this size when arranged. These values are always greater than or equal to SM_CXMINIMIZED and SM_CYMINIMIZED.
SM_CXMINTRACK, SM_CYMINTRACK Minimum tracking width and height of a window, in pixels. The user cannot drag the window frame to a size smaller than these dimensions. A window can override these values by processing the WM_GETMINMAXINFO message.
SM_CXSCREEN, SM_CYSCREEN Width and height of the screen of the primary display monitor, in pixels. These are the same values obtained by calling GetDeviceCaps(hdcPrimaryMonitor, HORZRES/VERTRES).
SM_CXSIZE, SM_CYSIZE Width and height of a button in a window's caption or title bar, in pixels.
SM_CXSIZEFRAME, SM_CYSIZEFRAME Thickness of the sizing border around the perimeter of a window that can be resized, in pixels. SM_CXSIZEFRAME is the width of the horizontal border, and SM_CYSIZEFRAME is the height of the vertical border. Same as SM_CXFRAME and SM_CYFRAME.
SM_CXSMICON, SM_CYSMICON Recommended dimensions of a small icon, in pixels. Small icons typically appear in window captions and in small icon view.
SM_CXSMSIZE SM_CYSMSIZE Dimensions of small caption buttons, in pixels.
SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN Width and height of the virtual screen, in pixels. The virtual screen is the bounding rectangle of all display monitors. The SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN metrics are the coordinates of the top-left corner of the virtual screen. Windows NT, Windows 95:  This value is not supported.
SM_CXVSCROLL, SM_CYVSCROLL Width of a vertical scroll bar, in pixels; and height of the arrow bitmap on a vertical scroll bar, in pixels.
SM_CYCAPTION Height of a caption area, in pixels.
SM_CYKANJIWINDOW For double byte character set versions of the system, this is the height of the Kanji window at the bottom of the screen, in pixels.
SM_CYMENU Height of a single-line menu bar, in pixels.
SM_CYSMCAPTION Height of a small caption, in pixels.
SM_CYVTHUMB Height of the thumb box in a vertical scroll bar, in pixels.
SM_DBCSENABLED TRUE or nonzero if User32.dll supports DBCS; FALSE or zero otherwise. Windows Me/98/95:   TRUE or nonzero if the double-byte character-set (DBCS) version of User.exe is installed; FALSE or zero otherwise.
SM_DEBUG TRUE or nonzero if the debug version of User.exe is installed; FALSE or zero otherwise.
SM_IMMENABLED TRUE or nonzero if Input Method Manager/Input Method Editor features are enabled; FALSE or zero otherwise. Windows NT, Windows Me/98/95:  This value is not supported. SM_IMMENABLED indicates whether the system is ready to use a Unicode-based IME on a Unicode application. To ensure that a language-dependent IME works, check SM_DBCSENABLED and the system ANSI code page. Otherwise the ANSI-to-Unicode conversion may not be performed correctly, or some components like fonts or registry setting may not be present.
SM_MENUDROPALIGNMENT TRUE or nonzero if drop-down menus are right-aligned with the corresponding menu-bar item; FALSE or zero if the menus are left-aligned.
SM_MIDEASTENABLED TRUE if the system is enabled for Hebrew and Arabic languages.
SM_MOUSEPRESENT TRUE or nonzero if a mouse is installed; FALSE or zero otherwise.
SM_MOUSEWHEELPRESENT TRUE or nonzero if a mouse with a wheel is installed; FALSE or zero otherwise. Windows 3.51 and earlier, Windows 95:  This value is not supported.
SM_NETWORK Least significant bit is set if a network is present; otherwise, it is cleared. The other bits are reserved for future use.
SM_PENWINDOWS TRUE or nonzero if the Microsoft Windows for Pen computing extensions are installed; FALSE or zero otherwise.
SM_REMOTECONTROL This system metric is used in a Terminal Services environment. Its value is TRUE if the current session is remotely controlled; FALSE otherwise. Windows 2000/NT, Windows Me/98/95:  This value is not supported.
SM_REMOTESESSION This system metric is used in a Terminal Services environment. If the calling process is associated with a Terminal Services client session, the return value is TRUE or nonzero. If the calling process is associated with the Terminal Server console session, the return value is zero. Windows NT 4.0 SP3 and earlier, Windows Me/98/95:  This value is not supported.
SM_SECURE TRUE if security is present; FALSE otherwise.
SM_SAMEDISPLAYFORMAT TRUE if all the display monitors have the same color format, FALSE otherwise. Note that two displays can have the same bit depth, but different color formats. For example, the red, green, and blue pixels can be encoded with different numbers of bits, or those bits can be located in different places in a pixel's color value. Windows NT, Windows 95:  This value is not supported.
SM_SHOWSOUNDS TRUE or nonzero if the user requires an application to present information visually in situations where it would otherwise present the information only in audible form; FALSE, or zero, otherwise.
SM_SHUTTINGDOWN TRUE if the current session is shutting down; FALSE otherwise. Windows 2000/NT, Windows Me/98/95:  This value is not supported.
SM_SLOWMACHINE TRUE if the computer has a low-end (slow) processor; FALSE otherwise.
SM_SWAPBUTTON TRUE or nonzero if the meanings of the left and right mouse buttons are swapped; FALSE or zero otherwise.
SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN Coordinates for the left side and the top of the virtual screen. The virtual screen is the bounding rectangle of all display monitors. The SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN metrics are the width and height of the virtual screen. Windows NT, Windows 95:  This value is not supported.


BOOL GetTextMetrics(
    
  HDC hdc,                        // 当前的设备描述表句柄
    
  LPTEXTMETRIC lptm   // 指向TEXTMETRICS结构对象的指针
);

  GetTextMetrics函数利用当前选择字体的各种度量值来填充由lptm参数所指向的缓冲区,函数如果运行成功则返回TURE,失败则返回FALSE。

  TEXTMETRIC结构定义在WinGdi.h头文件中。

  结构如下:

typedef struct tagTEXTMETRICA
{
    LONG        tmHeight;
    LONG        tmAscent;
    LONG        tmDescent;
    LONG        tmInternalLeading;
    LONG        tmExternalLeading;
    LONG        tmAveCharWidth;
    LONG        tmMaxCharWidth;
    LONG        tmWeight;
    LONG        tmOverhang;
    LONG        tmDigitizedAspectX;
    LONG        tmDigitizedAspectY;
    BYTE         tmFirstChar;
    BYTE        tmLastChar;
    BYTE        tmDefaultChar;
    BYTE        tmBreakChar;
    BYTE        tmItalic;
    BYTE        tmUnderlined;
    BYTE        tmStruckOut;
    BYTE        tmPitchAndFamily;
    BYTE        tmCharSet;
} TEXTMETRICA, *PTEXTMETRICA, NEAR *NPTEXTMETRICA, FAR *LPTEXTMETRICA;

typedef struct tagTEXTMETRICW
{
    LONG        tmHeight;
    LONG        tmAscent;
    LONG        tmDescent;
    LONG        tmInternalLeading;
    LONG        tmExternalLeading;
    LONG        tmAveCharWidth;
    LONG        tmMaxCharWidth;
    LONG        tmWeight;
    LONG        tmOverhang;
    LONG        tmDigitizedAspectX;
    LONG        tmDigitizedAspectY;
    WCHAR      tmFirstChar;
    WCHAR      tmLastChar;
    WCHAR      tmDefaultChar;
    WCHAR      tmBreakChar;
    BYTE        tmItalic;
    BYTE        tmUnderlined;
    BYTE        tmStruckOut;
    BYTE        tmPitchAndFamily;
    BYTE        tmCharSet;
} TEXTMETRICW, *PTEXTMETRICW, NEAR *NPTEXTMETRICW, FAR *LPTEXTMETRICW;

  该结构拥有大约20个属性,这些属性的值的单位取决于设备描述表的映射方式,默认情况下是MM_TEXT,对于TextOut输出来说我们只需要用到前7个,他们的单位是像素。

LONG        tmHeight;           // 字符基准线上下最大纵向高度,是tmAscenttmDescent之和。
LONG        tmAscent;       // 字符基准线以上所占的高度。
LONG        tmDescent;          // 字符基准线以下所占的高度。
LONG        tmInternalLeading;   // 内部间距,也是重音符号出现的地方。
LONG        tmExternalLeading;  // 行距
LONG        tmAveCharWidth;    // 小写字母的加权平均宽度,对于大写字母来说可以用小写字母的加权平均宽度乘以150%计算出来。
LONG        tmMaxCharWidth;    // 字符中字宽字符的宽度。

  字符的纵向大小是由TEXTMETRIC结构的前五个属性决定的。

  具体情况见下图:

  字体的大小是取决于当前屏幕的分辨率或是所选字体本身的默认大小的,在编写应用程序的时候不要把字体的大小以猜想的方式固定了,因为字体的大小是可变化的,利用GetTextMetrics函数动态的获取它们才是正确的。

格式化输出

  如果要使用TextOut函数在Windows应用程序中输出多行文字,我们就必须先取得字符的宽度和高度,通常我们在窗口消息处理函数中处理WM_CREATE消息的时候调用GetTextMetrics函数来获取他们,之所以放到WM_CREATE的部分调用,是因为当系统启动后,系统字体的大小就不会发生改变,我们只需要调用一次GetTextMetrics就可以了。

具体方法如下:

  大家可能对上面代码中的,tm.tmPitchAndFamily & 1 ? 3 : 2,这样的写法感到疑惑,TEXTMETRICS结构的tmPitchAndFamily属性是用来判断字体是否为变宽字体的,如果是等宽字体那么它的低位就为0,如果为变宽字体那么它的低位就为1,所以通过位运算我们可以判断是否为变宽字体,如果是就进行乘以150%的操作。

  看到这里,我们已经了解了TextOut函数输出所需要的所有知识,下面我们将以一个完整的例子对它进一步的学习。

实例练习

  例子目的是要将GetSystemMetrics函数所需要的参数以及这些参数的含义和返回值,以字符串的方式并格式化后循环输出在屏幕上。

  该例程我们分为两个文件,一个是用来保存GetSystemMetrics函数所需索引的头文件,另一个则是程序的主文件,代码的详细解释见代码的注释部分。

代码如下:  

/* sysmetrics.h 头文件 */

#define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0]))       

struct 

{

         int Index; // 索引

         TCHAR* szLabel;// 索引的字符串表示

         TCHAR* szDesc;// 索引含义       

} 

sysmetrics [ ] =

{

         SM_CXSCREEN, TEXT("SM_CXSCREEN"), TEXT("Screen width in pixels"), 

         SM_CYSCREEN, TEXT("SM_CYSCREEN"), TEXT("Screen height in pixels"),  

SM_CXVSCROLL, TEXT("SM_CXVSCROLL"), TEXT("Vertical scroll width"), 

         SM_CYHSCROLL, TEXT("SM_CYHSCROLL"), TEXT("Horizontal scroll height"), 

         SM_CYCAPTION, TEXT("SM_CYCAPTION"), TEXT("Caption bar height"), 

SM_CXBORDER, TEXT("SM_CXBORDER"), TEXT("Window border width"), 

SM_CYBORDER, TEXT("SM_CYBORDER"), TEXT("Window border height"),   

SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"), TEXT("Dialog window frame width"), 

SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"), TEXT("Dialog window frame height"),  

SM_CYVTHUMB, TEXT("SM_CYVTHUMB"), TEXT("Vertical scroll thumb height"), 

         SM_CXHTHUMB, TEXT("SM_CXHTHUMB"), TEXT("Horizontal scroll thumb width"), 

SM_CXICON, TEXT("SM_CXICON"), TEXT("Icon width"),       

SM_CYICON, TEXT("SM_CYICON"), TEXT("Icon height"),       

SM_CXCURSOR, TEXT("SM_CXCURSOR"), TEXT("Cursor width"),       

SM_CYCURSOR, TEXT("SM_CYCURSOR"), TEXT("Cursor height"),       

SM_CYMENU, TEXT("SM_CYMENU"), TEXT("Menu bar height"),       

SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"), TEXT("Full screen client area width"),       

SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"), TEXT("Full screen client area height"),       

SM_CYKANJIWINDOW,TEXT("SM_CYKANJIWINDOW"), TEXT("Kanji window height"),       

SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"), TEXT("Mouse present flag"),       

SM_CYVSCROLL, TEXT("SM_CYVSCROLL"), TEXT("Vertical scroll arrow height"),       

SM_CXHSCROLL, TEXT("SM_CXHSCROLL"), TEXT("Horizontal scroll arrow width"),       

SM_DEBUG, TEXT("SM_DEBUG"), TEXT("Debug version flag"),       

SM_SWAPBUTTON,TEXT("SM_SWAPBUTTON"), TEXT("Mouse buttons swapped flag"),       

SM_CXMIN, TEXT("SM_CXMIN"), TEXT("Minimum window width"),       

SM_CYMIN, TEXT("SM_CYMIN"), TEXT("Minimum window height"),       

SM_CXSIZE, TEXT("SM_CXSIZE"), TEXT("Min/Max/Close button width"),       

SM_CYSIZE, TEXT("SM_CYSIZE"), TEXT("Min/Max/Close button height"),       

SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"), TEXT("Window sizing frame width"),       

SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"), TEXT("Window sizing frame height"),       

SM_CXMINTRACK, TEXT("SM_CXMINTRACK"), TEXT("Minimum window tracking width"),       

SM_CYMINTRACK, TEXT("SM_CYMINTRACK"), TEXT("Minimum window tracking height"),       

SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"), TEXT("Double click x tolerance"),       

SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"), TEXT("Double click y tolerance"),       

SM_CXICONSPACING, TEXT("SM_CXICONSPACING"), TEXT("Horizontal icon spacing"),       

SM_CYICONSPACING, TEXT("SM_CYICONSPACING"), TEXT("Vertical icon spacing"),       

SM_MENUDROPALIGNMENT, TEXT("SM_MENUDROPALIGNMENT"), TEXT("Left or right menu drop"),       

SM_PENWINDOWS, TEXT("SM_PENWINDOWS"), TEXT("Pen extensions installed"),       

SM_DBCSENABLED, TEXT("SM_DBCSENABLED"), TEXT("Double-Byte Char Set enabled"),       

SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"), TEXT("Number of mouse buttons"),       

SM_SECURE, TEXT("SM_SECURE"), TEXT("Security present flag"),       

SM_CXEDGE, TEXT("SM_CXEDGE"), TEXT("3-D border width"),       

SM_CYEDGE, TEXT("SM_CYEDGE"), TEXT("3-D border height"),       

SM_CXMINSPACING, TEXT("SM_CXMINSPACING"), TEXT("Minimized window spacing width"),       

SM_CYMINSPACING, TEXT("SM_CYMINSPACING"), TEXT("Minimized window spacing height"),       

SM_CXSMICON, TEXT("SM_CXSMICON"), TEXT("Small icon width"),       

SM_CYSMICON, TEXT("SM_CYSMICON"), TEXT("Small icon height"),       

SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"), TEXT("Small caption height"),       

SM_CXSMSIZE, TEXT("SM_CXSMSIZE"), TEXT("Small caption button width"),       

SM_CYSMSIZE, TEXT("SM_CYSMSIZE"), TEXT("Small caption button height"),       

SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"), TEXT("Menu bar button width"),       

SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"), TEXT("Menu bar button height"),       

SM_ARRANGE, TEXT("SM_ARRANGE"), TEXT("How minimized windows arranged"),       

SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"), TEXT("Minimized window width"),       

SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"), TEXT("Minimized window height"),       

SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"), TEXT("Maximum draggable width"),       

SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"), TEXT("Maximum draggable height"),       

SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"), TEXT("Width of maximized window"),       

SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"), TEXT("Height of maximized window"),       

SM_NETWORK, TEXT("SM_NETWORK"), TEXT("Network present flag"),       

SM_CLEANBOOT, TEXT("SM_CLEANBOOT"), TEXT("How system was booted"),       

SM_CXDRAG, TEXT("SM_CXDRAG"), TEXT("Avoid drag x tolerance"),       

SM_CYDRAG, TEXT("SM_CYDRAG"), TEXT("Avoid drag y tolerance"),       

SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"), TEXT("Present sounds visually"),       

SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"), TEXT("Menu check-mark width"),       

SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"), TEXT("Menu check-mark height"),       

SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"), TEXT("Slow processor flag"),       

SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"), TEXT("Hebrew and Arabic enabled flag"),       

SM_MOUSEWHEELPRESENT, TEXT("SM_MOUSEWHEELPRESENT"), TEXT("Mouse wheel present flag"),       

SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"), TEXT("Virtual screen x origin"),       

SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"), TEXT("Virtual screen y origin"),       

SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"), TEXT("Virtual screen width"),       

SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"), TEXT("Virtual screen height"),       

SM_CMONITORS, TEXT("SM_CMONITORS"), TEXT("Number of monitors"),       

SM_SAMEDISPLAYFORMAT, TEXT("SM_SAMEDISPLAYFORMAT"), TEXT("Same color format flag")

}; 

/* main.cpp 主文件 */

#include <windows.h>

#include <stdio.h>

#include "system.h" 

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

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) 

static char szAppName[] = TEXT("HelloWin");

// 预先定义一个c风格字符串,稍后用于设置窗口类名称。

         WNDCLASS wndclass; // 定义窗口类对象。

         wndclass.style          = CS_HREDRAW | CS_VREDRAW;

         wndclass.lpfnWndProc   = WndProc ;

// 指定窗口的处理函数为WndProcWndProc将处理windows消息。

         wndclass.cbClsExtra     = 0; // 窗口类无扩展

         wndclass.cbWndExtra    = 0; // 窗口实例无扩展

         wndclass.hInstance      = hInstance; // 指定当前应用程序实例句柄,也就是程序当前的标识号。

         wndclass.hIcon          = LoadIcon (NULL,IDI_APPLICATION);

         wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW) ;

         wndclass.hbrBackground  = (HBRUSH)GetStockObject (WHITE_BRUSH); // 背景画刷颜色。

         wndclass.lpszMenuName  = NULL;

         wndclass.lpszClassName = szAppName; // 窗口类对象的名称。

//-----------------------------------------------------------------------------------------

         RegisterClass (&wndclass); 

//--------------------------  实例化过程  -------------------------------------------------

HWND   hwnd ;

//创建用于保存窗口句柄的对象,窗口句柄是系统识别不同窗口的依据,它只是个代号。

    hwnd = CreateWindow(

                                                                 szAppName,          // 窗口类名称。

                                                                 "GDI 入门",           // 窗口标题。

                                                                 WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL,  // 窗口样式。

                                                                 CW_USEDEFAULT,    // 初始的窗口x轴位置。

                                                                 CW_USEDEFAULT,    // 初始的窗口y轴位置。

                                                                 CW_USEDEFAULT,     // 初始的窗口x轴大小。

                                                                 CW_USEDEFAULT,     // 初始的窗口y轴大小。

                                                                 NULL,                 // 父窗口句柄。

                                                                 NULL,                 // 窗口功能表句柄。

                                                                 hInstance,              // 应用程序实例句柄。

                                                                 NULL                  // 建立参数,这个参数可以存取后面程序中可能引用到的资料。

                                                        );

    ShowWindow(hwnd, iCmdShow);

    UpdateWindow (hwnd);

//-----------------------------------------------------------------------------------------

//----------------------------  消息循环  -------------------------------------------------

    MSG    msg ; // 建立消息对象。

    while (GetMessage (&msg, NULL, 0, 0))

    {

                            TranslateMessage (&msg);

                            //把虚拟键盘消息转换到字符消息,满足键盘输入的需要,参数为msg消息对象的地址。

                            DispatchMessage (&msg);

                            //把当前的消息发送到消息处理函数中去。

    }

//----------------------------------------------------------------------------------------

    return msg.wParam;//返回消息结构中的wParam成员信息。

}   

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

// 窗口消息处理函数。

{

static int  iLFx, iCFx, iFy ;

HDC hdc; // 创建设备描述句柄对象。

PAINTSTRUCT ps; // 创建绘制结构对象。

         int i;

         TCHAR szBuffer [10]; // 创建字符数组用于保存GetSystemMetrics函数返回的值。

         TEXTMETRIC  tm; // 创建TEXTMETRIC结构对象。

         char szChar[200];

         switch (message)

         {

                   case WM_CREATE: // 当窗口创建的时候获得WM_CREATE消息。

                            hdc = GetDC (hwnd); // 获取设备描述表句柄。

                            GetTextMetrics (hdc, &tm);// 调用GetTextMetrics函数获取字符大小相关信息。

                            iLFx = tm.tmAveCharWidth;

                            // 获得小写字符的平均宽度,小写字母的加权平均值就是字符的平均宽度。

                            iCFx = (tm.tmPitchAndFamily & 1 ? 3 : 2) * iLFx / 2;

// 获得大写字符的平均宽度,对于变宽字体而言,大写字符的平均宽度是iLFx乘以150%,而如果是等宽字体,那么iCFx就等于iLFx

                            iFy = tm.tmHeight + tm.tmExternalLeading;

                            // 字符高度等于字符准线上下最大纵向高度加行距。

                            ReleaseDC (hwnd, hdc) ;

                            return 0 ;

                   case WM_PAINT://通知窗口更新显示区域的信息

                            hdc = BeginPaint (hwnd, &ps);

                            SetTextColor(hdc,RGB(255,0,0));

                            for (i = 0; i<NUMLINES; i++)

                            //通过获取的define定义的NUMLINES进行迭代,以循环输出字符串。

                            {

                                     TextOut (hdc, 0, iFy*i, sysmetrics[i].szLabel,lstrlen (sysmetrics[i].szLabel));

                                     /*

                                            第一个输出由于是从0位置开始,所以x轴为0y轴高度使用字符高度乘以循环次数获得。

                                     */

                                     TextOut (hdc, 22*iCFx, iFy*i, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));

                                     /*

                                               第二个输出由于字符串的起始点有了变化所以必须空出第一列字符串的宽度。

22*iCFx的是由第一列最长字符串长度加上两个空位得来,空位只是为了美观而多加上的。

                                     */

                                     SetTextAlign (hdc, TA_RIGHT | TA_TOP);

                                     TextOut (hdc, 22*iCFx+40*iLFx, iFy*i, szBuffer,wsprintf (szBuffer, TEXT("%5d"),GetSystemMetrics (sysmetrics[i].Index)));

                                     SetTextAlign (hdc, TA_LEFT | TA_TOP);

                                     /*

第三个输出主要是输出GetSystemMetrics函数返回的数值,但其中的确有些微妙。

由于变宽字符由左向右对齐数值的很难实现,所以我们换个思维而选择从右向左的方式对齐。

调用SetTextAlign (hdc, TA_RIGHT | TA_TOP);可以让对齐方式转变为右向左对齐。

22*iCFx+40*iLFx,包含了前两列的宽度,它的值成为了第三列字符串的x轴结束位置。

输出完成后调用SetTextAlign (hdc, TA_LEFT | TA_TOP);让对齐方式恢复默认,以便下一行的输出。

                                     */

                            }

            EndPaint (hwnd, &ps);

            MessageBox(NULL,"此对话框以下遮盖的部分需要更新!","覆盖区域",0);

            return 0;

                   case WM_DESTROY:

// 当窗口销毁的时候会返回此信息,比如ALT+F4或关闭窗口的时候,系统默认调用DestroyWindow()函数撤消窗口。

                            PostQuitMessage (0);

                            return 0;

         }

  return DefWindowProc (hwnd, message, wParam, lParam);//处理不于处理的消息

}

 


 

case WM_CREATE:

         hdc = GetDC (hwnd);         // 取得设备描述表句柄。

         GetTextMetrics (hdc, &tm);  // 调用GetTextMetrics函数将获得的信息保存在缓冲区中。

         iLFx = tm.tmAveCharWidth;

// 获得小写字符的平均宽度,小写字母的加权平均值就是字符的平均宽度。

         iCFx = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;

// 获得大写字符的平均宽度,对于变宽字体而言,大写字符的平均宽度是iLFx乘以150%,而如果是等宽字体,那么iCFx就等于iLFx

         iFy = tm.tmHeight + tm.tmExternalLeading; // 字符高度等于字符准线上下最大纵向高度加行距。

         ReleaseDC (hwnd, hdc) ; // 释放设备描述表句柄。

         return 0;

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多