23. Device Contexts II: Types of Device Contexts

Page 390
  23. Device Contexts II: Types of Device Contexts  
   
  There are actually four different types of device contexts:  
 
  Display
These DCs are used to draw on windows or on the screen.
 
 
  Printer
These DCs are used to draw to a printer or plotter.
 
 
  Memory
These DCs support drawing operations on a bitmap.
 
 
  Information
These DCs support the retrieval of device data, such as printer data, and thus use fewer resources than DCs that draw.
 
   
  We will discuss each of these types of DCs.  
 
  Information Device Contexts  
   
  The Win32 GDI supports a special type of device context called an information device context. These DCs are used to retrieve default device data. Information DCs are ''lightweight" DCs that do not require much in the way of resources. Neither can they be used for drawing operations as the name implies, they are strictly informational. An information DC is created using the CreateIC function:  
   HDC CreateIC(        LPCTSTR lpszDriver,         // pointer to string specifying driver name        LPCTSTR lpszDevice,         // pointer to string specifying device name        LPCTSTR lpszOutput,         // pointer to string specifying port or file name        CONST DEVMODE *lpdvmInit    // pointer to optional initialization data      );
Page 391
   
  The DEVMODE structure holds all sorts of information about a device:  
 
  Type DEVMODE
   dmDeviceName As String * CCHDEVICENAME       ' CCHDEVICENAME = 32
   dmSpecVersion As Integer
   dmDriverVersion As Integer
   dmSize As Integer
   dmDriverExtra As Integer
   dmFields As Long
   dmOrientation As Integer
   dmPaperSize As Integer
   dmPaperLength As Integer
   dmPaperWidth As Integer
   dmScale As Integer
   dmCopies As Integer
   dmDefaultSource As Integer
   dmPrintQuality As Integer
   dmColor As Integer
   dmDuplex As Integer
   dmYResolution As Integer
   dmTTOption As Integer
   dmCollate As Integer
   dmFormName As String *                          ' CCHFORMNAME = 32
   dmUnusedPadding As Integer
   dmBitsPerPel As Long
   dmPelsWidth As Long
   dmPelsHeight As Long
   dmDisplayFlags As Long
   dmDisplayFrequency As Long
   dmICMMethod As Long
   dmICMIntent As Long
   dmMediaType As Long
   dmDitherType As Long
   dmReserved1 As Long
   dmReserved2 As Long
End Type
 
   
  To illustrate, the following code creates an information DC for a printer and then uses GetCurrentObject and GetObject to get the face name of the printer's default font:  
 
  Sub GetPrinterInfo()
  Dim dc As Long
  Dim hObj As Long
  Dim f As LOGFONT

  dc = CreateIC(vbNullString, "HP LaserJet 4000 Series PS", vbNullString, 0)
  hObj = GetCurrentObject (dc, OBJ_FONT)
  GetObjectAPI hObj, LenB(f), f
 
 

Page 392
 
    Debug.Print "Face:" & StrConv(f.lfFaceName, vbUnicode)
  DeleteDC dc
End Sub
 
 
  Memory Device Contexts  
   
  The Win32 GDI supports a special type of device context called a memory device context. This type of DC is used to draw to a bitmap in memory. This bitmap must be compatible with another device context; that is, the bitmap must have the same dimensions and color format as the bitmap associated with a device. For this reason, a memory DC is sometimes called a compatible DC. We can create a memory device context by calling the CreateCompatibleDC function:  
 
  HDC CreateCompatibleDC(
  HDC hdc      // handle to a device context
);
 
   
  This function creates a memory device context that is compatible with the device context given by the hdc parameter. The return value is a handle to the memory DC.  
   
  It is important to note that when a memory DC is first created, its bitmap is just a one-pixel-by-one-pixel placeholder. Before we can begin drawing, we must select a bitmap with the appropriate width and height into the device context, by calling the SelectObject function.  
   
  One of the main uses of memory device contexts is to preserve a bitmap during other operations. For instance, as we have seen, Visual Basic creates and maintains a persistent bitmap in a memory device context for any form or picture box for which the AutoRedraw property is set to True. In this way, when the form or picture box needs repainting (if it is resized, for instance), VB can just map the persistent bitmap to the control.  
   
  Another important use of memory device contexts is to perform complex drawing operations in memory, which is faster than drawing directly to an output device. When drawing is completed, the entire bitmap image can be quickly transferred to the device using the BitBlt function. Let us illustrate.  
   
  The following code creates a memory DC that is compatible with a picture box (pic). It then colors each pixel in the memory bitmap with a random color. Finally, the bitmap is transferred to the picture box in one BitBlt operation. If you run this code with the line dcComp = pic.hdc commented and uncommented, you can see the difference in behavior between drawing directly to the picture box and using a memory device context. (The difference in performance is not that great in this simple example.)  
Page 393
 
  Public Sub MemoryDCExample()

' Requires a picture box named pic

' Save DC for later restoration
hDCPic = SaveDC(pic.hdc)

' Create compatible memory DC
dcComp = CreateCompatibleDC(pic.hdc)

"dcComp = pic.hdc

WidthInPixels = pic.Width / Screen.TwipsPerPixelX
HeightInPixels = pic.Height / Screen.TwipsPerPixelY

' Create bitmap
hBitmap = CreateCompatibleBitmap(pic.hdc, WidthInPixels, HeightInPixels)
lDummy = SelectObject(dcComp, hBitmap)

' Random pixel colors between 0 and 2^24 - 1
Randomize Timer
For r = 0 To WidthInPixels - 1
   For c = 0 To HeightInPixels - 1
      l = Rnd * (2 ^ 24 - 1)
      SetPixel dcComp, r, c, l
   Next
Next

BitBlt pic.hdc, 0, 0, WidthInPixels, HeightInPixels, dcComp, 0, 0, SRCCOPY

RestoreDC pic.hdc, hDCPic
DeleteDC dcComp
DeleteObject hBitmap

End Sub
 
 
  Printer Device Contexts  
   
  The Win32 GDI provides a special type of device context called a printer device context that can be used when printing with a printer or drawing with a plotter.  
   
  We can get access to a printer device context by calling CreateDC. When finished printing, the printer DC should be destroyed, using DeleteDC (not ReleaseDC).  
   
  The CreateDC function is:  
   HDC CreateDC(        LPCTSTR lpszDriver,         // pointer to string specifying driver name        LPCTSTR lpszDevice,         // pointer to string specifying device name        LPCTSTR lpszOutput,         // must set this to NULL        CONST DEVMODE *lpInitData   // pointer to optional printer data      );
Page 394
   
  In VB, we can write:  
 
  Declare Function CreateDC Lib "gdi32" Alias "CreateDCA" ( _
   ByVal lpDriverName As String, _
   ByVal lpDeviceName As String, _
   ByVal lpOutput As String, _
   lpInitData As DEVMODE _    ' can be null
) As Long
 
   
  Under Windows 9x, the lpszDriver parameter is generally ignored and should be set to NULL. The one exception is that CreateDC will return a display device context if we set this parameter to the string 'DISPLAY,' in which case all other parameters are ignored. Under Windows NT, this parameter should be a string equal to the string 'DISPLAY' (to get a display DC) or the name of the print spooler 'WINSPOOL.'  
   
  The lpszDevice parameter is a string that specifies the name of the output device, as shown by the Windows Print Manager.  
   
  The lpszOutput parameter is not used and should be set to NULL.  
   
  The lpInitData parameter points to a DEVMODE structure containing device-specific initialization data for the device driver. To use the default initialization values, set this parameter to 0. The DocumentProperties function will retrieve this structure filled in for a particular device.  
   
  The DEVMODE structure is a bit of a nightmare. In VB, it has the following declaration:  
 
  Type DEVMODE
   dmDeviceName As String * CCHDEVICENAME       ' CCHDEVICENAME = 32
   dmSpecVersion As Integer
   dmDriverVersion As Integer
   dmSize As Integer
   dmDriverExtra As Integer
   dmFields As Long
   dmOrientation As Integer
   dmPaperSize As Integer
   dmPaperLength As Integer
   dmPaperWidth As Integer
   dmScale As Integer
   dmCopies As Integer
   dmDefaultSource As Integer
   dmPrintQuality As Integer
   dmColor As Integer
   dmDuplex As Integer
   dmYResolution As Integer
   dmTTOption As Integer
   dmCollate As Integer
   dmFormName As String * CCHFORMNAME             ' CCHFORMNAME = 32
   dmUnusedPadding As Integer
   dmBitsPerPel As Long
   dmPelsWidth As Long
 
 

Page 395
 
     dmPelsHeight As Long
   dmDisplayFlags As Long
   dmDisplayFrequency As Long
   dmICMMethod As Long
   dmICMIntent As Long
   dmMediaType As Long
   dmDitherType As Long
   dmReserved1 As Long
   dmReserved2 As Long
End Type
 
   
  We will certainly not discuss all of these members. Note, however, that the dmDeviceName member is the name of the device as reported by the Print Manager.  
   
  Enumerating Printers  
   
  Windows implements the EnumPrinters enumeration function that enumerates the available printers on a system:  
  BOOL EnumPrinters(       DWORD Flags,         // types of printer objects to enumerate       LPTSTR Name,         // name of printer object       DWORD Level,         // specifies type of printer into structure       LPBYTE pPrinterEnum, // pointer to buffer to receive printer into structures       DWORD cbBuf,         // size, in bytes, of the buffer       LPDWORD pcbNeeded,   // pointer to variable with no. of bytes                            // copied (or required)       LPDWORD pcReturned   // pointer to variable with no. of printer                            // info. structures copied     );
   
  In VB, this is:  
 
  Declare Function EnumPrinters Lib "winspool.drv" Alias "EnumPrintersA" ( _
   ByVal flags As Long, _
   ByVal Name As String, _
   ByVal Level As Long, _
   pPrinterEnum As Long, _
   ByVal cdBuf As Long, _
   pcbNeeded As Long, _
   pcReturned As Long _
) As Long
 
   
  The parameters to this function are rather complicated, and we refer the reader to the documentation for all of the gory details. Instead, let us do an example.  
   
  Essentially, the EnumPrinters function returns an array of printer information structures one for each printer, along with supporting strings. There are five different printer information structures. The one we are interested in is:  
 
  Public Type PRINTER_INFO_2
     pServerName As Long
     pPrinterName As Long
 
 

Page 396
 
       pShareName As Long
     pPortName As Long
     pDriverName As Long
     pComment As Long
     pLocation As Long
     pDevMode As Long          ' pointer to DEVMODE
     pSepFile As Long
     pPrintProcessor As Long
     pDatatype As Long
     pParameters As Long
     pSecurityDescriptor As Long
     Attributes As Long
     Priority As Long
     DefaultPriority As Long
     StartTime As Long
     UntilTime As Long
     Status As Long
     cJobs As Long
     AveragePPM As Long
End Type
 
   
  Note that the EnumPrinters function is not recursive it behaves like EnumProcesses rather than EnumWindows (both of which we have already discussed). Thus, the only way to tell whether we have allocated a sufficiently large buffer is to try it. The return value pcbNeeded points to a long that tells us the number of bytes needed. If we didn't allocate a sufficient number of bytes, then we must try again!  
   
  Here is the code to list some printer values:  
 
  Sub ListPrinters ()

Dim lNeeded As Long
Dim lReturned As Long
Dim lData() As Long
Dim pi2 As PRINTER_INFO_2
Dim dm As DEVMODE
Dim i As Integer

ReDim lData(0 To 4000)

EnumPrinters PRINTER_ENUM_LOCAL, vbNullString, 2, _
   lData(0), 4000, lNeeded, lReturned
Debug.Print "Needed: " & lNeeded
Debug.Print "Returned: " & lReturned

If lNeeded > 4000 Then MsgBox "Increase buffer size."

For i = 0 To lReturned - 1

   Debug.Print "** Printer " & i

   ' Copy i-th PRINTER_INFO_2 structure
   CopyMemory ByVal VarPtr(pi2), _
      ByVal VarPtr(lData(i * LenB(pi2) / 4)), LenB(pi2)
 
 

Page 397
 
     Debug.Print "Name: " & LPSTRtoBSTR(pi2.pPrinterName)
   Debug.Print "Port: " & LPSTRtoBSTR(pi2.pPortName)
   Debug.Print "Driver: " & LPSTRtoBSTR(pi2.pDriverName)
   Debug.Print "Comment: " & LPSTRtoBSTR(pi2.pDriverName)

   CopyMemory ByVal VarPtr(dm), ByVal pi2.pDevMode, LenB(dm)
   Debug.Print "Driver Ver: " & dm.dmDriverVersion

Next

End Sub
 
   
  The output on my system is:  
 
  Needed: 2008
Returned: 2
** Printer 0
Name: HP LaserJet 4000 Series PS
Port: LPT1:
Driver: HP LaserJet 4000 Series PS
Comment: HP LaserJet 4000 Series PS
Driver Ver: 3
** Printer 1
Name: EPSON Stylus COLOR 800
Port: LPT2:
Driver: EPSON Stylus COLOR 800
Comment: EPSON Stylus COLOR 800
Driver Ver: 0
 
   
  Getting the default printer  
   
  Under Windows 95, we can set the first parameter of EnumPrinters to PRINTER_ENUM_DEFAULT to get information about the default printer. However, this does not work under Windows NT! Also, it does not appear that EnumPrinters lists the printers in any special order. (It would have been nice if the default printer was always the first one enumerated.) Thus, there does not seem to be a way to get the default printer under Windows NT by using EnumPrinters.  
   
  However, we can get information about the default printer, using the GetProfile-String function. Here is the code:  
 
  Public Function GetDefaultPrinter()

Dim sDefPrinter As String
sDefPrinter = String(1024, vbNullChar)
GetProfileString "windows", "device", "xxx", sDefPrinter, 1024
Debug.Print sDefPrinter

End Function
 
   
  On my system, the output is:  
 
  HP LaserJet 4000 Series PS, winspool,LPT1:  
Page 398
   
  Enumerating Printer Drivers  
   
  Windows also implements the EnumPrinterDrivers enumeration function that enumerates the available printer drivers on a system:  
  BOOL EnumPrinterDrivers(       LPTSTR pName,        // pointer to server name       LPTSTR pEnvironment, // pointer to environment name       DWORD Level,         // structure level       LPBYTE pDriverInfo,  // pointer to an array of structures       DWORD cbBuf,         // size, in bytes, of array       LPDWORD pcbNeeded,   // pointer to number of bytes copied (or required)       LPDWORD pcReturned   // pointer to number of DRIVER_INFO. structures     );
   
  In VB, we can write this as:  
 
  Declare Function EnumPrinterDrivers Lib "winspool.drv" _
Alias "EnumPrinterDriversA" ( _
   ByVal pName As String, _
   ByVal pEnvironment As String, _
   ByVal Level As Long, _
   pDriverInfo As Long, _
   ByVal cdBuf As Long, _
   pcbNeeded As Long, _
   pcReturned As Long _
) As Long
 
   
  Once again, this is a complex function, and we just consider an example. Of the three different DRIVER_INFO structures, we consider only:  
 
  Type DRIVER_INFO_2
     cVersion As Long
     pName As Long
     pEnvironment As Long
     pDriverPath As Long
     pDataFile As Long
     pConfigFile As Long
End Type
 
   
  Here is some code to enumerate printer drivers:  
 
  Sub ListDrivers()

Dim lNeeded As Long
Dim lReturned As Long
Dim lData() As Long
Dim di2 As DRIVER_INFO_2
Dim i As Integer

ReDim lData(0 To 4000)

EnumPrinterDrivers vbNullString, vbNullString, 2, lData(0), _
   4000, lNeeded, lReturned
Debug.Print "Needed: " & lNeeded
Debug.Print "Returned: " & lReturned
 
 

Page 399
 
  If lNeeded > 4000 Then MsgBox "Increase buffer size."

For i = 0 To lReturned - 1

   Debug.Print "** Driver " & i

   CopyMemory ByVal VarPtr(di2), _
      ByVal VarPtr(lData(i * LenB(di2) / 4)), LenB(di2)

   Debug.Print "Name: " & LPSTRtoBSTR(di2.pName)
   Debug.Print "Version: " & LPSTRtoBSTR(di2.cVersion)
   Debug.Print "Path: " & LPSTRtoBSTR(di2.pDriverPath)
   Debug.Print "DataFile: " & LPSTRtoBSTR(di2.pDataFile)
   Debug.Print "ConfigFile: " & LPSTRtoBSTR(di2.pConfigFile)
Next

End Sub
 
   
  The output on my system is:  
 
  Needed: 854
Returned: 2
** Driver 0
Name: HP LaserJet 4000 Series PS
Version:
Path: C:\WINNT\System32\spool\DRIVERS\W32X86\2\PSCRIPT.DLL
DataFile: C:\WINNT\System32\spool\DRIVERS\W32X86\2\HP4000_6.PPD
ConfigFile: C:\WINNT\System32\spool\DRIVERS\W32X86\2\PSCRPTUI.DLL
** Driver 1
Name: EPSON Stylus COLOR 800
Version:
Path: C:\WINNT\System32\spool\DRIVERS\W32X86\2\E_CPDJ33.DLL
DataFile: C:\WINNT\System32\spool\DRIVERS\W32X86\2\E_C93J33.DLL
ConfigFile: C:\WINNT\System32\spool\DRIVERS\W32X86\2\E_CUDJ33.DLL
 
   
  Printing  
   
  The steps involved in printing using the Windows GDI are these:  
   
  1. Create a printer device context using CreateDC. As we have seen, this function requires the printer name.  
   
  2. Call the StartDoc function to start the document:  
 
  Declare Function StartDoc Lib "gdi32" Alias "StartDocA" ( _
   ByVal hdc As Long, _
   lpdi As DOCINFO _
) As Long
 
   
  3. Call the StartPage function to start a new page:  
 
  Declare Function StartPage Lib "gdi32" ( _
   ByVal hdc As Long _
) As Long
 
Page 400
   
  4. Call the desired GDI drawing functions to print the data to a memory buffer.  
   
  5. Call the EndPage function, which causes Windows to send the page to the printer:  
 
  Declare Function EndPage Lib "gdi32" ( _
   ByVal hdc As Long _
) As Long
 
   
  6. Repeat the previous three steps for each page.  
   
  7. Call the EndDoc GDI function (not VB's EndDoc function) to end the document:  
 
  Declare Function EndDocAPI Lib "gdi32" Alias "EndDoc" ( _
   ByVal hdc As Long _
) As Long
 
   
  For example, the following code will print an ellipse on my LaserJet 4000 printer:  
 
  Sub PrintIt()

Dim printDC As Long
Dim di As DOCINFO

' Initialize DOCINFO
di.cbSize = LenB(di)
di.lpszDocName = "document"
di.lpszOutput = vbNullString ' or name of file to print to
di.lpszDataType = vbNullString

' Create printer DC
printDC = CreateDC(vbNullString, "HP LaserJet 4000 Series PS", vbNullString, 0)

' Start document and page
StartDoc printDC, di
StartPage printDC

' Print ellipse
Ellipse printDC, 0, 0, 600, 600

' End page and document
EndPage printDC
EndDocAPI printDC

' Delete printer DC
DeleteDC printDC

End Sub
 
   
  There is quite a bit more to printing under the Win32 GDI than we have discussed here, but this introduction should point you in the right direction. Indeed, there are about 80 different printer-related GDI functions! Have fun.  
Page 401
 
  Display Device Contexts  
   
  Display device contexts are used to draw to windows or to the screen. There are several types of display device contexts, as shown in Table 23-1.  
Table 23-1. Types of Display Contexts
Type Drawing Scope Cached? Obtain Via Class Style Comments
Common Client area Yes GetDC or BeginPaint None (default) Uses no additional memory
Class Client area No GetDC   CS_CLASSDC Only one DC for all windows of this class
Parent Entire window and parent window Yes GetDC or BeginPaint CS_PARENTDC Intended for child windows only
Private Client area No GetDC CS_OWNDC New 800 byte DC created for each window
Window Entire window Yes GetWindow DC or GetDCEx NA  


   
  Note that VB supplies a private DC through its hDC property, so generally that is what the VB programmer will use. However, for completeness' sake. we will briefly discuss all of the different types of DCs shown in Table 23-1.  
   
  Cached and Noncached Display Contexts  
   
  As you can see from Table 23-1, some display contexts are cached. Windows maintains a cache of device contexts for common, parent, and window DCs. Windows will create new cached DCs when required, but cached DCs consume memory from the application's default heap, so care must be taken not to use too many cached DCs at one time. In fact, cached DCs are intended to be used quickly and released immediately after use, by calling ReleaseDC (or EndPaint). Also, cached DCs are intended to be used in cases where few changes need to be made to the default attributes. Each time a cached DC is released back into the cache, its settings are returned to the default. (Incidentally, 16-bit Windows had a limit of five cached DCs.)  
   
  On the other hand, the noncached DCs are intended to be created and held by an application indefinitely. These DCs provide better performance since they are readily available once created. Each noncached DC consumes 800 bytes of memory.  
Page 402
   
  Classes and Display Contexts  
   
  As we have seen, in order to draw to a window, we must first obtain a device context, using a function such as GetDC, GetDCEx, or GetWindowDC. The type of display device context that is supplied by Windows as a result of a call to one of these functions depends upon the style of the window class from which the window was created.  
   
  Recall that, in order to register a window class, the RegisterClass function requires a WNDCLASS structure. The style member of that structure is used (in part) to specify the type of default DC. We describe the possibilities next (and see Table 23-1).  
   
  Common display device contexts  
   
  A common DC is supplied by Windows (in response to a request for a DC) as the default when no class style specifies the DC type. Common device contexts are particularly efficient because they do not require additional memory or system resources.  
   
  Common DCs draw only in the client area of the window. Thus, the coordinate system's origin is initially set to the upper-left corner of the client area. Also, the clipping region is set to the client area. This means that any drawing that would extend beyond the client area is clipped (not drawn). If an application requests a common device context using the BeginPaint function, the update region is also set.  
   
  Private display contexts  
   
  The CS_OWNDC class style specifies that each window of this class gets a private display context. Windows stores each private display context in the GDI's memory heap. It is not necessary to release a private display context (using ReleaseDC). These DCs should be used only with mapping mode MM_TEXT to ensure proper window erasing (we will discuss mapping modes in Chapter 24, Device Contexts III: Coordinate Systems).  
   
  As mentioned earlier, VB's hDC property returns the handle to a private device context.  
   
  Class display contexts  
   
  The CS_CLASSDC class style specifies that the windows of that class share a single display context, which is called the class display context. Class display contexts offer some of the benefits of private display contexts, but save resources over private DCs. These DCs should be used only with mapping mode MM_TEXT to ensure proper window erasing (we will discuss mapping modes in Chapter 24).  
Page 403
   
  Parent display contexts  
   
  The CS_PARENTDC class style specifies that each window of that class use its parent window's display context. Thus, as with class DCs, multiple windows share one display context, thus saving resources. The main benefit of parent DCs is that they are fast.  
 
  Coordinate Systems  
   
  In general, the GDI drawing functions require that coordinates be specified for a drawing operation. (For instance, we cannot draw a line without specifying where the line starts and where it ends.) This requires a coordinate system. The subject of Windows coordinate systems can be quite confusing, so let us see if we can make some sense of it.  
   
  Physical Devices  
   
  We begin with the simple observation that the ultimate goal of the GDI is to draw graphical objects (including text) on a physical device. Physical devices include:  
   
  The printable area of a piece of paper in a printer  
   
  The display area of a monitor  
   
  A complete window  
   
  The client area of a window  
   
  Physical space and physical coordinate systems  
   
  Each physical device has a natural physical coordinate system determined by the way images are displayed in the physical space of the device. In all cases, the physical coordinate system has the following characteristics (see Figure 23-1):  
 
  Origin
The origin is in the upper-left-hand corner of the device.
 
 
  Orientation
The horizontal axis increases, moving to the left, and the vertical axis increases, moving towards the bottom.
 
 
  Units
The units of the physical coordinate system are pixels (for the monitor or a window) or printer dots (for the printer). We may refer to both of these units simply as pixels.
 
   
  Note that when the device is the client area of a window, the physical coordinate system has its origin in the upper-left corner of the client area of the window.  
Page 404
   
  0404-01.gif  
   
  Figure 23-1.
Natural physical coordinate system
 
   
  Incidentally, it is tempting to refer to the physical coordinate system as the device coordinate system. However, Windows has usurped this term for a coordinate system that is identical with the physical coordinate system except that its origin need not be in the upper-left-hand corner of the device. Drat.  
   
  Monitor inches  
   
  Of course, a 600 dpi laser printer prints 600 dots per inch, so it is easy to translate printer dots to the more useful inches or millimeters. However, things are not nearly this simple for monitors.  
   
  The problem stems from the way in which resolution is described on monitors. Printer resolution is given in dots per inch, which is the most  useful procedure, whereas monitor resolution is given in terms of the total number of pixels in the vertical and horizontal direction. The problem is that Windows has no way of knowing the actual physical dimensions of the monitor, so it cannot convert the horizontal and vertical resolutions to pixels per physical inch.  
   
  To deal with this problem, Windows defines what is sometimes referred to as a logical inch. As we will see, this seldom corresponds to an actual physical inch on a monitor. It is, in fact, Windows' monitor-independent version of a physical inch. (For a printer, Windows understands actual physical inches.)  
   
  Unfortunately, the term logical inch sounds like it is connected with the upcoming notion of logical coordinate system, so from now on we will refer to logical inches as monitor inches (since they apply only to monitors anyway). We emphasize that this terminology is not standard.  
   
  For a printer, a physical inch corresponds to a certain number of dots. For instance, for a 600 dpi laser printer, we have:  
 
  1 physical inch = 600 printer dots  
   
  In a sense, we could have defined an inch to be 600 dots on a 600 dpi laser printer. This, of course, is totally inappropriate, so we won't do it. However, monitor inches are defined in this way, that is, in pixels, as in:  
 
  1 monitor inch = 96 pixels  
Page 405
   
  In this way, for instance, a programmer can specify a line segment of length 2 monitor inches, and Windows (or the Windows device driver) will know how many pixels (2 96 = 192) to use in drawing the line segment, just as it would know that in order to print the line segment on a 600 dpi laser printer, it would require 2 600 = 1200 dots.  
   
  How are monitor inches actually defined? The Windows Control Panel's Display applet allows the user to select from one of two display font sizes small fonts or large fonts. Windows uses this choice to determine the size of a monitor inch. In particular, if the user chooses the small font, Windows sets the monitor inch at 96 pixels. On the other hand, if the user chooses large fonts in the Display applet, Windows sets a monitor inch at 120 pixels. Thus,  
 
  1 monitor inch = 96 pixels 'small font setting in Control Panel
1 monitor inch = 120 pixels 'large font setting in Control Panel
 
   
  As we have said, generally speaking, monitor inches are quite different from physical inches. For instance, I am writing this book on a 21-inch monitor running at 1600 by 1200 with large fonts. Accordingly, Windows sets the monitor inch to 120 pixels, and so the display area has monitor-inch dimensions:  
 
  monitor width = 1600/120 = 13.3 monitor inches
monitor height = 1200/120 = 10 monitor inches
 
   
  However, the physical dimensions of the display area are in fact 14.9 wide by 11.2 high. Thus, in this case, a monitor inch is approximately 1.12 physical inches. On the other hand, suppose that I were to raise the resolution to 1800 by 1440, which is the next setting on my display adapter. The monitor dimensions would become:  
 
  monitor width = 1800/120 = 15 monitor inches
monitor height = 1440/120 = 12 monitor inches
 
   
  Now, a monitor inch in the horizontal direction measures 14.9/15 = 0.99 physical inches, but in the vertical direction, it measures 11.2/12 = 0.93 inches! This difference occurs because the resolution of 1800 by 1440 has aspect ratio 1800/1440 = 1.25, rather than the more usual aspect ratio of 1.33 achieved by the resolutions 640 by 480, 800 by 600, 1024 by 768, and 1600 by 1200 as well as the physical display dimensions on my monitor.  
   
  The GetDeviceCaps function (short for Get Device Capacities) can be used to get the number of pixels per monitor inch, along with other values. The declaration for this function is:  
 
  int GetDeviceCaps(
   HDC hdc,    // device-context handle
   int nIndex  // index of capability to query
);
 
Page 406
   
  Here are some of the most useful values of nIndex with respect to the monitor:  
 
  HORZSIZE
Width, in millimeters, of the physical screen
 
 
  VERTSIZE
Height, in millimeters, of the physical screen
 
 
  HORZRES
Width, in pixels, of the screen
 
 
  VERTRES
Height, in raster lines, of the screen
 
 
  LOGPIXELSX
Number of pixels per logical inch along the screen width
 
 
  LOGPIXELSY
Number of pixels per logical inch along the screen height
 
   
  For example, here is the output for my 21-inch monitor:  
 
  HORZ SIZE: 320
VERT SIZE: 240
HORZ RES: 1600
VERT RES: 1200
LOGPIXELSX: 120
LOGPIXELSY: 120
 
   
  Actually, this is confusing. The correct formulas relating these values should be:  
 
  HORZ SIZE = 25.4 * HORZ RES/LOGPIXELSX
VERT SIZE = 25.4 * VERT RES/LOGPIXELSY
 
   
  Under Windows 9x. the values would be correct, but Windows NT (which I am running) always uses the values 320 and 240, for reasons unknown to me.  


WIN32 API Programming with Visual Basic
Win32 API Programming with Visual Basic
ISBN: 1565926315
EAN: 2147483647
Year: 1999
Pages: 31
Authors: Steven Roman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net