The DEVCAPS1 Program

The DEVCAPS1 program, shown in Figure 11-1, displays all the information available from the GetDeviceCaps function for either the video display or the selected printer. (A second version of this program, called DEVCAPS2, will be presented in Chapter 15.) If you change the current printer using the Windows Control Panel program, DEVCAPS1 updates the printer information.

DEVCAPS1.MAK

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

# DEVCAPS1.MAK make file

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

devcaps1.exe : devcaps1.obj devcaps.obj devcaps1.def devcaps1.res

link devcaps1 devcaps, /align:16, NUL, /nod slibcew libw, devcaps1

rc devcaps1.res

devcaps1.obj : devcaps1.c devcaps1.h

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

devcaps.obj : devcaps.c

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

devcaps1.res : devcaps1.rc devcaps1.h

rc -r devcaps1.rc

DEVCAPS1.C

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

DEVCAPS1.C -- Displays Device Capability Information

(c) Charles Petzold, 1990

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

#include <windows.h>

#include <string.h>

#include "devcaps1.h"

void DoBasicInfo (HDC, HDC, short, short) ; // in DEVCAPS.C

void DoOtherInfo (HDC, HDC, short, short) ;

void DoBitCodedCaps (HDC, HDC, short, short, short) ;

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

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

static char szAppName[] = "DevCaps" ;

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 = szAppName ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hwnd = CreateWindow (szAppName, "Device Capabilities",

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 ;

}

HDC GetPrinterIC ()

{

char szPrinter [64] ;

char *szDevice, *szDriver, *szOutput ;

GetProfileString ("windows", "device", "", szPrinter, 64) ;

if ((szDevice = strtok (szPrinter, "," )) &&

(szDriver = strtok (NULL, ", ")) &&

(szOutput = strtok (NULL, ", ")))

return CreateIC (szDriver, szDevice, szOutput, NULL) ;

return NULL ;

}

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

{

static short cxChar, cyChar, nCurrentDevice = IDM_SCREEN,

nCurrentInfo = IDM_BASIC ;

HDC hdc, hdcInfo ;

HMENU hMenu ;

PAINTSTRUCT ps ;

TEXTMETRIC tm ;

switch (message)

{

case WM_CREATE :

hdc = GetDC (hwnd) ;

SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

GetTextMetrics (hdc, &tm) ;

cxChar = tm.tmAveCharWidth ;

cyChar = tm.tmHeight + tm.tmExternalLeading ;

ReleaseDC (hwnd, hdc) ;

return 0 ;

case WM_COMMAND :

hMenu = GetMenu (hwnd) ;

switch (wParam)

{

case IDM_SCREEN :

case IDM_PRINTER :

CheckMenuItem (hMenu, nCurrentDevice, MF_UNCHECKED) ;

nCurrentDevice = wParam ;

CheckMenuItem (hMenu, nCurrentDevice, MF_CHECKED) ;

InvalidateRect (hwnd, NULL, TRUE) ;

return 0 ;

case IDM_BASIC :

case IDM_OTHER :

case IDM_CURVE :

case IDM_LINE :

case IDM_POLY :

case IDM_TEXT :

CheckMenuItem (hMenu, nCurrentInfo, MF_UNCHECKED) ;

nCurrentInfo = wParam ;

CheckMenuItem (hMenu, nCurrentInfo, MF_CHECKED) ;

InvalidateRect (hwnd, NULL, TRUE) ;

return 0 ;

}

break ;

case WM_DEVMODECHANGE :

InvalidateRect (hwnd, NULL, TRUE) ;

return 0 ;

case WM_PAINT :

hdc = BeginPaint (hwnd, &ps) ;

SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

if (nCurrentDevice == IDM_SCREEN)

hdcInfo = CreateIC ("DISPLAY", NULL, NULL, NULL) ;

else

hdcInfo = GetPrinterIC () ;

if (hdcInfo)

{

switch (nCurrentInfo)

{

case IDM_BASIC :

DoBasicInfo (hdc, hdcInfo, cxChar, cyChar) ;

break ;

case IDM_OTHER :

DoOtherInfo (hdc, hdcInfo, cxChar, cyChar) ;

break ;

case IDM_CURVE :

case IDM_LINE :

case IDM_POLY :

case IDM_TEXT :

DoBitCodedCaps (hdc, hdcInfo, cxChar, cyChar,

nCurrentInfo - IDM_CURVE) ;

break ;

}

DeleteDC (hdcInfo) ;

}

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

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

}

DEVCAPS.C

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

DEVCAPS.C -- Display routines for DEVCAPS1 and DEVCAPS2

(c) Charles Petzold, 1990

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

#include <windows.h>

#include <string.h>

#include <stdio.h>

typedef struct

{

short nMask ;

char *szMask ;

char *szDesc ;

}

BITS ;

void DoBasicInfo (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)

{

static struct

{

short nIndex ;

char *szDesc ;

}

info [] =

{

HORZSIZE, "HORZSIZE Width in millimeters:",

VERTSIZE, "VERTSIZE Height in millimeters:",

HORZRES, "HORZRES Width in pixels:",

VERTRES, "VERTRES Height in raster lines:",

BITSPIXEL, "BITSPIXEL Color bits per pixel:",

PLANES, "PLANES Number of color planes:",

NUMBRUSHES, "NUMBRUSHES Number of device brushes:",

NUMPENS, "NUMPENS Number of device pens:",

NUMMARKERS, "NUMMARKERS Number of device markers:",

NUMFONTS, "NUMFONTS Number of device fonts:",

NUMCOLORS, "NUMCOLORS Number of device colors:",

PDEVICESIZE, "PDEVICESIZE Size of device structure:",

ASPECTX, "ASPECTX Relative width of pixel:",

ASPECTY, "ASPECTY Relative height of pixel:",

ASPECTXY, "ASPECTXY Relative diagonal of pixel:",

LOGPIXELSX, "LOGPIXELSX Horizontal dots per inch:",

LOGPIXELSY, "LOGPIXELSY Vertical dots per inch:",

SIZEPALETTE, "SIZEPALETTE Number of palette entries:",

NUMRESERVED, "NUMRESERVED Reserved palette entries",

COLORRES, "COLORRES Actual color resolution:"

} ;

char szBuffer [80] ;

short i, nLine ;

for (i = 0 ; i < sizeof info / sizeof info [0] ; i++)

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

sprintf (szBuffer, "%-40s%8d", info[i].szDesc,

GetDeviceCaps (hdcInfo, info[i].nIndex))) ;

}

void DoOtherInfo (HDC hdc, HDC hdcInfo, short cxChar, short cyChar)

{

static BITS clip [] =

{

CP_RECTANGLE, "CP_RECTANGLE", "Can clip to rectangle:"

} ;

static BITS raster [] =

{

RC_BITBLT, "RC_BITBLT", "Capable of simple BitBlt:",

RC_BANDING, "RC_BANDING", "Requires banding support:",

RC_SCALING, "RC_SCALING", "Requires scaling support:",

RC_BITMAP64, "RC_BITMAP64", "Supports bitmaps >64K:",

RC_GDI20_OUTPUT, "RC_GDI20_OUTPUT", "Has 2.0 output calls:",

RC_DI_BITMAP, "RC_DI_BITMAP", "Supports DIB to memory:",

RC_PALETTE, "RC_PALETTE", "Supports a palette:",

RC_DIBTODEV, "RC_DIBTODEV", "Supports bitmap conversion:",

RC_BIGFONT, "RC_BIGFONT", "Supports fonts >64K:",

RC_STRETCHBLT, "RC_STRETCHBLT", "Supports StretchBlt:",

RC_FLOODFILL, "RC_FLOODFILL", "Supports FloodFill:"

} ;

static char *szTech [] = { "DT_PLOTTER (Vector plotter)",

"DT_RASDISPLAY (Raster display)",

"DT_RASPRINTER (Raster printer)",

"DT_RASCAMERA (Raster camera)",

"DT_CHARSTREAM (Character-stream, PLP)",

"DT_METAFILE (Metafile, VDM)",

"DT_DISPFILE (Display-file)" } ;

char szBuffer [80] ;

short i ;

TextOut (hdc, cxChar, cyChar, szBuffer,

sprintf (szBuffer, "%-24s%04XH",

"DRIVERVERSION:", GetDeviceCaps (hdcInfo, DRIVERVERSION))) ;

TextOut (hdc, cxChar, 2 * cyChar, szBuffer,

sprintf (szBuffer, "%-24s%-40s",

"TECHNOLOGY:", szTech [GetDeviceCaps (hdcInfo, TECHNOLOGY)])) ;

TextOut (hdc, cxChar, 4 * cyChar, szBuffer,

sprintf (szBuffer, "CLIPCAPS (Clipping capabilities)")) ;

for (i = 0 ; i < sizeof clip / sizeof clip [0] ; i++)

TextOut (hdc, 9 * cxChar, (i + 6) * cyChar, szBuffer,

sprintf (szBuffer, "%-16s%-28s %3s",

clip[i].szMask, clip[i].szDesc,

GetDeviceCaps (hdcInfo, CLIPCAPS) & clip[i].nMask ?

"Yes" : "No")) ;

TextOut (hdc, cxChar, 8 * cyChar, szBuffer,

sprintf (szBuffer, "RASTERCAPS (Raster capabilities)")) ;

for (i = 0 ; i < sizeof raster / sizeof raster [0] ; i++)

TextOut (hdc, 9 * cxChar, (i + 10) * cyChar, szBuffer,

sprintf (szBuffer, "%-16s%-28s %3s",

raster[i].szMask, raster[i].szDesc,

GetDeviceCaps (hdcInfo, RASTERCAPS) & raster[i].nMask ?

"Yes" : "No")) ;

}

void DoBitCodedCaps (HDC hdc, HDC hdcInfo, short cxChar, short cyChar,

short nType)

{

static BITS curves [] =

{

CC_CIRCLES, "CC_CIRCLES", "circles:",

CC_PIE, "CC_PIE", "pie wedges:",

CC_CHORD, "CC_CHORD", "chord arcs:",

CC_ELLIPSES, "CC_ELLIPSES", "ellipses:",

CC_WIDE, "CC_WIDE", "wide borders:",

CC_STYLED, "CC_STYLED", "styled borders:",

CC_WIDESTYLED, "CC_WIDESTYLED", "wide and styled borders:",

CC_INTERIORS, "CC_INTERIORS", "interiors:"

} ;

static BITS lines [] =

{

LC_POLYLINE, "LC_POLYLINE", "polylines:",

LC_MARKER, "LC_MARKER", "markers:",

LC_POLYMARKER, "LC_POLYMARKER", "polymarkers",

LC_WIDE, "LC_WIDE", "wide lines:",

LC_STYLED, "LC_STYLED", "styled lines:",

LC_WIDESTYLED, "LC_WIDESTYLED", "wide and styled lines:",

LC_INTERIORS, "LC_INTERIORS", "interiors:"

} ;

static BITS poly [] =

{

PC_POLYGON, "PC_POLYGON", "alternate fill polygon:",

PC_RECTANGLE, "PC_RECTANGLE", "rectangle:",

PC_TRAPEZOID, "PC_TRAPEZOID", "winding number fill polygon:",

PC_SCANLINE, "PC_SCANLINE", "scanlines:",

PC_WIDE, "PC_WIDE", "wide borders:",

PC_STYLED, "PC_STYLED", "styled borders:",

PC_WIDESTYLED, "PC_WIDESTYLED", "wide and styled borders:",

PC_INTERIORS, "PC_INTERIORS", "interiors:"

} ;

static BITS text [] =

{

TC_OP_CHARACTER, "TC_OP_CHARACTER", "character output precision:",

TC_OP_STROKE, "TC_OP_STROKE", "stroke output precision:",

TC_CP_STROKE, "TC_CP_STROKE", "stroke clip precision:",

TC_CR_90, "TC_CP_90", "90-degree character rotation:",

TC_CR_ANY, "TC_CR_ANY", "any character rotation:",

TC_SF_X_YINDEP, "TC_SF_X_YINDEP", "scaling independent of x and y:",

TC_SA_DOUBLE, "TC_SA_DOUBLE", "doubled character for scaling:",

TC_SA_INTEGER, "TC_SA_INTEGER", "integer multiples for scaling:",

TC_SA_CONTIN, "TC_SA_CONTIN", "any multiples for exact scaling:",

TC_EA_DOUBLE, "TC_EA_DOUBLE", "double-weight characters:",

TC_IA_ABLE, "TC_IA_ABLE", "italicizing:",

TC_UA_ABLE, "TC_UA_ABLE", "underlining:",

TC_SO_ABLE, "TC_SO_ABLE", "strikeouts:",

TC_RA_ABLE, "TC_RA_ABLE", "raster fonts:",

TC_VA_ABLE, "TC_VA_ABLE", "vector fonts:"

} ;

static struct

{

short nIndex ;

char *szTitle ;

BITS (*pbits) [] ;

short nSize ;

}

bitinfo [] =

{

CURVECAPS, "CURVCAPS (Curve capabilities)",

(BITS (*)[]) curves, sizeof curves / sizeof curves [0],

LINECAPS, "LINECAPS (Line capabilities)",

(BITS (*)[]) lines, sizeof lines / sizeof lines [0],

POLYGONALCAPS, "POLYGONALCAPS (Polygonal capabilities)",

(BITS (*)[]) poly, sizeof poly / sizeof poly [0], TEXTCAPS, "TEXTCAPS (Text capabilities)",

(BITS (*)[]) text, sizeof text / sizeof text [0]

} ;

static char szBuffer [80] ;

BITS (*pbits) [] = bitinfo [nType].pbits ;

short nDevCaps = GetDeviceCaps (hdcInfo, bitinfo [nType].nIndex) ;

short i ;

TextOut (hdc, cxChar, cyChar, bitinfo [nType].szTitle,

strlen (bitinfo [nType].szTitle)) ;

for (i = 0 ; i < bitinfo [nType].nSize ; i++)

TextOut (hdc, cxChar, (i + 3) * cyChar, szBuffer,

sprintf (szBuffer, "%-16s %s %-32s %3s",

(*pbits)[i].szMask, "Can do", (*pbits)[i].szDesc,

nDevCaps & (*pbits)[i].nMask ? "Yes" : "No")) ;

}

DEVCAPS1.RC

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

DEVCAPS1.RC resource script

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

#include "devcaps1.h"

DevCaps MENU

{

POPUP "&Device"

{

MENUITEM "&Screen", IDM_SCREEN, CHECKED

MENUITEM "&Printer", IDM_PRINTER

}

POPUP "&Capabilities"

{

MENUITEM "&Basic Information", IDM_BASIC, CHECKED

MENUITEM "&Other Information", IDM_OTHER

MENUITEM "&Curve Capabilities", IDM_CURVE

MENUITEM "&Line Capabilities", IDM_LINE

MENUITEM "&Polygonal Capabilities",IDM_POLY

MENUITEM "&Text Capabilities", IDM_TEXT

}

}

DEVCAPS1.H

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

DEVCAPS1.H header file

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

#define IDM_SCREEN 1

#define IDM_PRINTER 2

#define IDM_BASIC 3

#define IDM_OTHER 4

#define IDM_CURVE 5

#define IDM_LINE 6

#define IDM_POLY 7

#define IDM_TEXT 8

DEVCAPS1.DEF

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

; DEVCAPS1.DEF module definition file

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

NAME DEVCAPS1

DESCRIPTION 'Displays Device Capability Info (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

The DEVCAPS1 Device menu lets you select either the screen or the printer. Because DEVCAPS1 needs only to obtain information about this device, it gets a handle to an information context (using the CreateIC function) rather than to a device context. Getting an information context handle for the video device is easy:

HIC = CreateIC ("DISPLAY", NULL, NULL, NULL) ;

However, an information context handle for the printer requires more complex code:

HDC GetPrinterIC ()

{

char szPrinter [64] ;

char *szDevice, *szDriver, *szOutput ;

GetProfileString ("windows", "device", "", szPrinter, 64) ;

if ((szDevice = strtok (szPrinter, ",")) &&

(szDriver = strtok (NULL, ", ")) &&

(szOutput = strtok (NULL, ", ")))

return CreateIC (szDriver, szDevice, szOutput, NULL) ;

return NULL ;

}

The selected printer is listed in the [windows] section of the WIN.INI file in the following format:

device=device name,driver filename,port

For an IBM Graphics printer connected to the LPT1 printer port, the WIN.INI line is:

device=IBM Graphics,IBMGRX,LPT1:

IBM Graphics is the name of the printer, and IBMGRX.DRV is the name of the driver file.

To get an information context (or device context) for the current printer, you must first obtain the character string following device= in WIN.INI by using the GetProfileString function. You must then parse this string into the three components: the device name, the driver filename, and the port. You can do this in various ways. I happened to use the C strtok function, which is designed for parsing character strings separated by delimiters such as commas and spaces. Note that the device name itself can have embedded blanks.

If you'd like to use DEVCAPS1 to examine the device capabilities of other printers, you can add printer driver files to your Windows subdirectory using the Control Panel program and then select each of these printers, one by one, as the current printer. Specify that the port the printer is connected to is ”NONE.“ An advantage of CreateIC over CreateDC is that CreateDC returns a device context handle only if the printer is attached to a port, whereas CreateIC doesn't care whether the printer is attached. To get an idea of the range of devices you'll be dealing with, you might want to obtain the GetDeviceCaps information for a few different types of printers, such as a simple nongraphics printer (the ”Generic/ Text Only“ printer), a sophisticated laser printer (the Apple LaserWriter Plus), and a plotter (the Hewlett-Packard ColorPro). DEVCAPS1 intercepts the WM_DEVMODECHANGE message that Control Panel sends to all applications to signal that the current printer has been changed.

The Capabilities menu in DEVCAPS1 lets you display one of six screens that show the GetDeviceCaps information. Much of the DEVCAPS1 code is dedicated to formatting this information. When you choose the Basic Information option from the Capabilities menu, the most important information is displayed, including the size of the display, the number of pure colors it can display, and the organization of display memory into color planes and color bits per pixel. Figure 11-2 shows this basic information for a VGA; Figure 11-3 shows the information for an Apple LaserWriter Plus.

When you choose the Other Information option from the Capabilities menu, the program displays the type of device (usually ”Raster device,“ ”Raster printer,“ or ”Vector plotter“) and gives some information that is crucial for using printers, as you'll discover when you come to Chapter 15. Figure 11-4 shows this display for an Apple LaserWriter Plus. The RC_BITBLT identifier indicates that this printer can accept bitmaps; text-only printers and plotters cannot, however, which means that some GDI functions won't work on them. The RC_BANDING identifier indicates that this IBM printer, like many printers, requires ”banding“ support, which means that the GDI module must print to the printer in segments, each occupying a small section of the page. Again, we'll explore these issues further in Chapter 16.

The other menu options in DEVCAPS display curve, line, polygon, and text capabilities. The information displayed indicates the type of graphics and text manipulation that the device driver can handle. However, this information is much more important to Windows itself than to application programs—if the driver lacks one of these capabilities and a program demands it, then the GDI module must assume the task. For instance, you don't have to check to see if your printer is capable of drawing ellipses before you draw an ellipse.

The information that you can obtain from GetDeviceCaps using the CURVECAPS, LINECAPS, POLYGONALCAPS, and TEXTCAPS parameters is encoded in bits in the return value. Although the Programmer's Reference doesn't indicate it, WINDOWS.H includes identifiers beginning with the letters CC, LC, PC, and TC to help you mask out the bits you want.