The Device menu of the DEVCAPS2 program includes an option called Device Mode. To use it, first select a printer from the Device menu, and then select Device Mode: Up pops a dialog box. Where did the dialog box come from? It is invoked by the printer driver, and_ at the very least—it requests that you make a choice of paper size. Most printer drivers also give you a choice of ”portrait“ or ”landscape“ mode. In portrait mode (often the default), the short side of the paper is the top; in landscape mode, the long side is the top. If you change this mode, the change is reflected in the information the DEVCAPS2 program obtains from the GetDeviceCaps function: The horizontal size and resolution are switched with the vertical size and resolution. Device Mode dialog boxes for color plotters can be quite extensive, requesting the colors of the pens installed in the plotter and the type of paper (or transparencies) being used.
All printer drivers contain an exported function called DeviceMode that invokes this dialog box and saves the information that the user enters. Some printer drivers store this in- formation in their own section of the WIN.INI file, and some don't. Those that store the information have access to it during the next Windows session.
Windows programs that allow the user a choice of printers generally call the DeviceMode function of the printer driver so that the user can make changes in preparation for printing. Calling this function from a program requires a technique that we'll learn more about in Chapter 19. Here's how DEVCAPS2 does it.
The program first obtains the name of the printer currently selected in the Device menu and saves it in a character array named szDevice:
GetMenuString (hMenu, nCurrentDevice, szDevice,
sizeof szDevice, MF_BYCOMMAND) ;
Then it obtains the driver name and output port of this device using GetProfileString. This information is stored in szDriver:
GetProfileString ("devices", szDevice, "",
szDriver, sizeof szDriver) ;
The output port is separated from the szDriver string using strtok, and the pointer is saved in szOutput:
szOutput = strtok (szDriver, ", ") ;
The szDriver string contains the name of the driver, which is the driver's filename without the .DRV extension. This statement creates the full name of the driver file and saves it in szDriverFile:
strcat (strcpy (szDriverFile, szDriver), ".DRV") ;
This driver file is a dynamic link library module. (Library modules are the subject of Chapter 19.) We can obtain a handle to this module (which is actually the instance handle of the module) by calling LoadLibrary. If LoadLibrary returns a value greater than or equal to 32, the function was successful. Otherwise, the return value indicates an MS-DOS error code.
The library can be freed by a call to FreeLibrary. If no other program is using this library, then it can be deleted from memory. DEVCAPS2 holds the library handle (or whatever was returned from LoadLibrary) in a static variable, so before trying to load a new library, it first frees the old one if the handle was valid:
if (hLibrary >= 32)
FreeLibrary (hLibrary) ;
hLibrary = LoadLibrary (szDriverFile) ;
Before proceeding, the program checks to see if this new handle is valid:
if (hLibrary >= 32)
It then calls GetProcAddress to obtain the address of the DeviceMode function:
lpfnDM = GetProcAddress (hLibrary, "DEVICEMODE") ;
The DeviceMode function can be called indirectly by prefacing it with an asterisk. The function is passed the window handle, library module handle, device name, and output port:
(*lpfnDM) (hwnd, hLibrary, (LPSTR) szDevice,
(LPSTR) szOutput) ;
This invokes the dialog box. Note that you must explicitly cast the strings into far pointers, because this function has no template in WINDOWS.H or anywhere else.
The currently loaded driver file is freed when the program terminates:
case WM_DESTROY :
if (hLibrary >= 32)
FreeLibrary (hLibrary) ;
The LoadLibrary call increments the library module's ”reference count“ (a number Windows maintains to indicate the number of programs using a module), and the FreeLibrary call decrements it. The library can be freed from memory when the reference count is 0. Calls to CreateDC and CreateIC for a printer driver also increment the reference count, and DeleteDC decrements it.