Putting It All Together

Now we seem to have everything we need to write a simple program that displays multiple lines of text on the screen. We know how to get a handle to a device context, how to use the TextOut function, and how to space text based on the size of a single character. The only thing left to do is to display something interesting.

The information available in the Windows GetSystemMetrics call looks interesting enough. This function returns information about the size of various graphical items in Windows, such as icons, cursors, caption bars, and scroll bars. These sizes vary with the display adapter and driver. GetSystemMetrics requires a single parameter called an ”index.“ This index is 1 of 37 integer identifiers defined in WINDOWS.H. GetSystemMetrics returns an integer, usually the size of the item specified in the parameter.

Let's write a program that displays all the information available from the GetSystemMetrics call in a simple one-line-per-item format. Working with this information is easier if we create a header file that defines an array of structures containing both the WINDOWS.H identifiers for the GetSystemMetrics index and the text we want to display for each value returned from the call. This header file is called SYSMETS.H and is shown in Figure 2-4.

SYSMETS.H

/*-----------------------------------------------

SYSMETS.H -- System metrics display structure

-----------------------------------------------*/

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

struct

{

int nIndex ;

char *szLabel ;

char *szDesc ;

}

sysmetrics [] =

{

SM_CXSCREEN, "SM_CXSCREEN", "Screen width in pixels",

SM_CYSCREEN, "SM_CYSCREEN", "Screen height in pixels",

SM_CXVSCROLL, "SM_CXVSCROLL", "Vertical scroll arrow width",

SM_CYHSCROLL, "SM_CYHSCROLL", "Horizontal scroll arrow height",

SM_CYCAPTION, "SM_CYCAPTION", "Caption bar height",

SM_CXBORDER, "SM_CXBORDER", "Border width",

SM_CYBORDER, "SM_CYBORDER", "Border height",

SM_CXDLGFRAME, "SM_CXDLGFRAME", "Dialog window frame width",

SM_CYDLGFRAME, "SM_CYDLGFRAME", "Dialog window frame height",

SM_CYVTHUMB, "SM_CYVTHUMB", "Vertical scroll thumb height",

SM_CXHTHUMB, "SM_CXHTHUMB", "Horizontal scroll thumb width",

SM_CXICON, "SM_CXICON", "Icon width",

SM_CYICON, "SM_CYICON", "Icon height",

SM_CXCURSOR, "SM_CXCURSOR", "Cursor width",

SM_CYCURSOR, "SM_CYCURSOR", "Cursor height",

SM_CYMENU, "SM_CYMENU", "Menu bar height",

SM_CXFULLSCREEN, "SM_CXFULLSCREEN", "Full-screen client window width",

SM_CYFULLSCREEN, "SM_CYFULLSCREEN", "Full-screen client window height",

SM_CYKANJIWINDOW, "SM_CYKANJIWINDOW", "Kanji window height",

SM_MOUSEPRESENT, "SM_MOUSEPRESENT", "Mouse present flag",

SM_CYVSCROLL, "SM_CYVSCROLL", "Vertical scroll arrow height",

SM_CXHSCROLL, "SM_CXHSCROLL", "Horizontal scroll arrow width",

SM_DEBUG, "SM_DEBUG", "Debug version flag",

SM_SWAPBUTTON, "SM_SWAPBUTTON", "Mouse buttons swapped flag",

SM_RESERVED1, "SM_RESERVED1", "Reserved",

SM_RESERVED2, "SM_RESERVED2", "Reserved",

SM_RESERVED3, "SM_RESERVED3", "Reserved",

SM_RESERVED4, "SM_RESERVED4", "Reserved",

SM_CXMIN, "SM_CXMIN", "Minimum window width",

SM_CYMIN, "SM_CYMIN", "Minimum window height",

SM_CXSIZE, "SM_CXSIZE", "Minimize/Maximize icon width",

SM_CYSIZE, "SM_CYSIZE", "Minimize/Maximize icon height",

SM_CXFRAME, "SM_CXFRAME", "Window frame width",

SM_CYFRAME, "SM_CYFRAME", "Window frame height",

SM_CXMINTRACK, "SM_CXMINTRACK", "Minimum tracking width of window",

SM_CYMINTRACK, "SM_CYMINTRACK", "Minimum tracking height of window",

SM_CMETRICS, "SM_CMETRICS", "Number of system metrics"

} ;

The program that displays this information is called SYSMETS1. The files required to create SYSMETS1.EXE (make file, C source code, and module definition file) are shown in Figure 2-5. Most of the code should look familiar by now. With the exception of the program name, the make file, resource script, and DEF file are identical to those for HELLOWIN. In SYSMETS1.C, WinMain is virtually identical to HELLOWIN.

SYSMETS1.MAK

#------------------------

# SYSMETS1.MAK make file

#------------------------

sysmets1.exe : sysmets1.obj sysmets1.def

link sysmets1, /align:16, NUL, /nod slibcew libw, sysmets1

rc sysmets1.exe

sysmets1.obj : sysmets1.c sysmets.h

cl -c -Gsw -Ow -W2 -Zp sysmets1.c

SYSMETS1.C

/*----------------------------------------------------

SYSMETS1.C -- System Metrics Display Program No. 1

(c) Charles Petzold, 1990

----------------------------------------------------*/

#include <windows.h>

#include "sysmets.h"

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

static char szAppName[] = "SysMets1" ;

HWND hwnd ;

MSG msg ;

WNDCLASS wndclass ;

if (!hPrevInstance)

{

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

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 = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = NULL ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hwnd = CreateWindow (szAppName, "Get System Metrics No. 1",

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 ;

}

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static short cxChar, cxCaps, cyChar ;

char szBuffer[10] ;

HDC hdc ;

short i ;

PAINTSTRUCT ps ;

TEXTMETRIC tm ;

switch (message)

{

case WM_CREATE :

hdc = GetDC (hwnd) ;

GetTextMetrics (hdc, &tm) ;

cxChar = tm.tmAveCharWidth ;

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

cyChar = tm.tmHeight + tm.tmExternalLeading ;

ReleaseDC (hwnd, hdc) ;

return 0 ;

case WM_PAINT :

hdc = BeginPaint (hwnd, &ps) ;

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

{

TextOut (hdc, cxChar, cyChar * (1 + i),

sysmetrics[i].szLabel,

lstrlen (sysmetrics[i].szLabel)) ;

TextOut (hdc, cxChar + 18 * cxCaps, cyChar * (1 + i),

sysmetrics[i].szDesc,

lstrlen (sysmetrics[i].szDesc)) ;

SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;

TextOut (hdc, cxChar + 18 * cxCaps + 40 * cxChar,

cyChar * (1 + i), szBuffer,

wsprintf (szBuffer, "%5d",

GetSystemMetrics (sysmetrics[i].nIndex))) ;

SetTextAlign (hdc, TA_LEFT | TA_TOP) ;

}

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

SYSMETS1.DEF

;-------------------------------------

; SYSMETS1.DEF module definition file

;-------------------------------------

NAME SYSMETS1

DESCRIPTION 'System Metrics Display No. 1 (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

Figure 2-6 shows SYSMETS1 running on a VGA. As you can see from the program's window, the screen width is 640 pixels and the screen height is 480 pixels. These two values, as well as many of the other values shown by the program, will be different for different types of video displays.