PRINTING, SPOOLING, AND ESCAPE

When you use a printer in Windows, you're actually initiating a complex interaction involving the GDI library module, the printer device driver library module (which has a .DRV extension), and the Windows Print Manager program (PRINTMAN.EXE), as well as some other modules that get into the act. Before we start programming for the printer, let's examine how this process works.

When an application program wants to begin using a printer, it first obtains a handle to the printer device context using CreateDC. This causes the printer device driver library module to be loaded into memory (if it's not present already) and to initialize itself. The program then calls the Escape subfunction named STARTDOC, which signals the beginning of a new document. The Escape function is handled by the GDI module. The GDI module calls the Control function (which is equivalent to Escape) in the printer device driver. The device driver performs some initialization and calls OpenJob, which is in the GDI module. The GDI module then loads the Windows Print Manager program into memory.

Following the STARTDOC Escape call, the program can make the appropriate GDI calls for the first page of the document. For example, if the program wants to draw an ellipse on the page, it calls Ellipse, just as it does when drawing an ellipse on the screen. The GDI module generally stores all these GDI calls in a disk-based metafile, which is located in the subdirectory indicated by the TEMP variable in the MS-DOS environment. (If no TEMP variable exists, Windows uses the root directory of the first fixed disk on the system.) The file begins with the characters ~MF and has a .TMP extension.

When the application program is finished with the GDI calls that define the first page, the program calls the NEWFRAME subfunction of Escape. Now the real work begins. The printer driver must translate the various drawing commands stored in the metafile into output for the printer. The printer output required to define a page of graphics can be very large, particularly if the printer has no high-level page-composition language. For example, a 300-dots-per-inch laser printer using 8-1/2-by-11-inch paper might require more than a megabyte of data to define one page of graphics.

For this reason, printer drivers often implement a technique called ”banding,“ which divides the page into rectangles called bands. (We'll examine banding later in this chapter.) The GDI module obtains the dimensions of each band from the printer driver. It then sets a clipping region equal to this band and calls the printer device driver Output function for each of the drawing functions contained in the metafile. This process is called ”playing the metafile into the device driver.“ The GDI module must play the entire metafile into the device driver for each band that the device driver defines on the page. After the process is completed, the metafile can be deleted.

For each band, the device driver translates these drawing functions into the output necessary to realize them on the printer. The format of this output will be specific to the printer. For dot-matrix printers, it will be a collection of control sequences, including graphics sequences. (For some assistance with constructing this output, the printer driver can call various ”helper“ routines also located in the GDI module.) For laser printers with a high-level page-composition language (such as PostScript), the printer output will be in this language.

The printer driver uses the WriteSpool function to pass the printer output for each band to the GDI module, which then stores this printer output in a temporary file also located in the TEMP subdirectory. This file begins with the characters ~SPL and has a .TMP extension. When the entire page is finished, the GDI module uses the SendMessage function to send a message to the Print Manager indicating that a new print job is ready. The application program then goes on to the next page. When the application is finished with all the pages it must print, it makes the ENDDOC Escape call to signal that the print job is completed. Figure 15-1 on the following page shows the interaction of the program, the GDI module, and the printer driver.

The Windows Print Manager program is a print spooler that relieves application programs of some of the work involved with printing. The GDI module loads the Print Manager (if it is not already loaded) automatically when a program begins printing. The GDI module then creates the files that contain printer output. The Print Manager's job is to send these files out to the printer. It is notified of a new print job by a message from the GDI

module. It then begins reading the file and transferring it directly to the printer. To transfer the files, the Print Manager uses various communications functions (OpenComm, WriteComm, and so forth included in the USER module) for the parallel or serial port that the printer is connected to. During the time that the Print Manager is writing the printer output to the output port, other Windows programs can function normally. When the Print Manager is done sending a file to a printer, it can delete the temporary file holding the output. This process is shown in Figure 15-2.

Most of this process is transparent to the application program. From the perspective of the program, ”printing“ occurs only during the time required for the GDI module to save all the printer output in disk files. After that, the program is freed up to do other things. The actual printing of the document becomes the Print Manager's responsibility rather than the program's. A user can direct the Print Manager to pause print jobs, to change their priority, or to cancel print jobs. This arrangement allows programs to ”print“ faster than would be possible if they were printing in real time and had to wait for the printer to finish one page before proceeding to the next.

Although I've described how printing works in general, there are some variations on this theme. One is that the Print Manager doesn't have to be present in order for Windows programs to use the printer. Normally, the [windows] section of WIN.INI contains this line:

Spooler=yes

But a user can change that to:

Spooler=no

If this line is present, the Print Manager doesn't allow itself to be executed.

Why would a user not want the Print Manager to be loaded? Well, perhaps the user has a hardware or software print spooler that works faster than the Print Manager. Or perhaps the printer is on a network that has its own spooler. The general rule is that one spooler is faster than two. Removing the Windows Print Manager would speed up printing, because the printer output doesn't have to be stored on disk. It can go right out to the printer and be intercepted by the external hardware or software print spooler.

If the Print Manager can't be loaded, the GDI module doesn't store the printer output from the device driver in a file. Instead, GDI itself sends the output directly to the parallel or serial printer port. Unlike the printing done by the Print Manager, the printing done by GDI has the potential of holding up the operation of application programs (particularly the program doing the printing) until the printing is completed.

Here's another variation on the general theme. Normally, the GDI module stores all the functions necessary to define a page in a metafile and then plays this metafile into the printer driver once for each band defined by the driver. If the printer driver doesn't require banding, however, the metafile isn't created; GDI simply passes the drawing functions directly to the driver. In a further variation, it is also possible for an application to assume responsibility for dividing printer output into bands. This makes the printing code in the application program more complex, but it relieves the GDI module of creating the metafile. Once again, GDI simply passes the functions for each band to the printer driver.

Now perhaps you're starting to see how printing from a Windows program might involve a bit more overhead than that required for using the video display. Several problems can occur—particularly if the GDI module runs out of disk space while creating the metafile or the printer output files. You can either get very involved in reporting these problems to the user and attempting to do something about them, or you can remain relatively aloof.

We'll examine several different approaches to printing in the pages that follow. But first things first—let's begin by obtaining a printer device context.