When the spooler determines that a particular spooled job can be printed, it calls the monitor’s implementations of StartDocPort, WritePort, and EndDocPort to print the job.
On Windows NT, a printer is actually a printer queue, or logical printer, that can dispatch print jobs to one of several ports associated with it. The spooler can use a printer queue of several associated ports to optimize printing. It can do this by making efficient use of the physical printers connected to the logical printer’s ports.
For example, the ports LPT1, LPT2, and LPT3 could all be associated with a single logical printer in one call to the Win32 AddPrinter routine. Then, when the spooler has a job that needs printing, it might attempt to dispatch the job to the physical printer connected to LPT1 by calling through the associated language monitor’s StartDocPort routine. Note that the language monitors associated with these three ports could all be different.
If the physical printer is busy with another job, or if it has problems such as an empty paper tray, it can return an error condition to the monitor. The language monitor’s StartDocPort function should then call the Win32 SetPort routine with the appropriate error code to notify the spooler of the problem. The spooler can then try sending the job to the next physical printer, attached to LPT2, in the queue. If this fails, the spooler can finally try printing to LPT3. The spooler will not send any more print jobs to a port that signals a failure until the error condition is cleared on it.
Language monitors might call WritePort (and/or ReadPort) on a port monitor outside of a StartDocPort/EndDocPort pair, for example, to check status on a printer. Port monitors that do not allow this can fail the call, and language monitors should be written to handle such a failure.
StartDocPort
The StartDocPort routine performs whatever tasks are necessary to start a print job on the specified port. A port monitor’s implementation might include tasks such as:
·Obtaining a handle to the printer to be used by calling the Win32 OpenPrinter function.
·Opening the port and performing any port setup required, such as initializing its end-of-file position for writing, by calling the Win32 CreateFile and SetEndOfFile functions.
·Allocating any resources required on a per print-job basis.
The language monitor’s StartDocPort routine might perform tasks such as:
·Validating the port monitor and printer with which the language monitor will communicate.
·Calling the port monitor’s StartDocPort function to perform any generic per print-job initializations.
·Allocating any resources required by the language monitor on a per print-job basis.
·Determining whether the printer understands the language monitor’s supported language and, if so, starting a thread in which to listen for unsolicited status information from the printer.
If a monitor’s StartDocPort routine cannot perform the setup required, it should clean up as necessary and return FALSE. The spooler retries StartDocPort as often as the application retries.
WritePort
The WritePort routine sends a block of print data to the printer. A WritePort implementation could send the data by calling the Win32 WriteFile function. The spooler calls WritePort as often as is necessary to complete sending the entire print job. The spooler determines the size of the data block sent in each WritePort call, so a monitor should make no assumptions about the block size.
A language monitor’s WritePort routine can add printer job control data to print data before sending it to the port monitor. When the language monitor returns to the spooler after adding bytes, such as printer control information, to the print data stream, it must not increment the byte count of data sent to the printer. For example, if your language monitor adds .25K of information to a 1K data stream before sending it to the port monitor, it must report sending only the original 1K of data when it returns.
EndDocPort
The EndDocPort function performs end-of-print-job tasks on the port. The spooler calls EndDocPort when there is no more data to send for the current print job.
A port monitor’s EndDocPort implementation should call the Win32 SetJob routine with dwCommand set to JOB_CONTROL_SENT_TO_PRINTER to notify the spooler that the entire print job has been sent to the printer. Note that a pure port monitor cannot guarantee that a job has actually been printed; it can only guarantee that it has sent the entire job to the printer.
If a print job has been sent to a bidirectional printer, a language monitor’s EndDocPort can start a thread that listens for true end-of-job notification; that is, notification from a bidirectional printer that it has printed the entire job. When the language monitor receives this notification, it should call the Win32 SetJob routine with dwCommand set to JOB_CONTROL_LAST_PAGE_EJECTED to provide the spooler with true end-of-job notification.
Other tasks that a port monitor’s EndDocPort implementation might perform include:
·Clearing any data that the port has cached by calling, for example, the Win32 FlushFileBuffers routine.
·Closing the printer by calling the Win32 ClosePrinter function.
·Freeing any per print-job resources allocated by StartDocPort.