How to get the status of a printer and a print job

The status of printers and print jobs are updated by the Win32 Spoolerduring the despool of a print job. At all other times, when that printer isnot despooling and reports no state information, the printer is consideredto be ready and idle.
More information
As referred to by the Win32 API, a "printer" is comprised of the printerdriver, the print queue, and the input/output path to the physical printer.The operating system treats a physical printer as merely the destination ofa print job generated by and passed through a system "Printer," referred toin the rest of this article as a Printer.

The most visible part of a Printer is a print queue. It is managed by thePrint Manager or the Printer folders in the Windows 95-style userinterfaces. The printer driver is the interface to the Printer that is usedby applications to create print jobs via printer DCs. The I/O path for aPrinter consists of several layers of system code culminating with a portmonitor.

The port monitor is the interface to the physical printer at the down-stream end of a system Printer and is responsible for transferring the dataof a print job across whatever connection exists to the physical printer.In the case of bi-directional printers, the port monitor would beresponsible for transferring data to and from the physical printer. Thisconnection, and the physical printer, are where errors occur. It is the jobof the port monitor to report those errors.

The Spooler does not query for the state of a physical printer to which aPrinter is connected. Instead, the state of a physical printer determinesthe success of a print job at the time it is despooled over the portmonitor. If some error occurs in this process, the error is reported by theport monitor and recorded in a print job's status information. The Spooler,in turn, propagates reasonable error information to the Printer Queue.

Consequently, a system Printer reports no status when the Printer queue isempty. In this state, the Printer is assumed ready to accept print jobs.This is a valid assumption even if the physical printer is in an errorstate such as off-line. The operating system considers the Printer ready toaccept print jobs even if, for some reason, it cannot complete delivery tothe physical printer. Such a circumstance is considered an error state inthe operating system that must be addressed by the user. It is notconsidered an error reportable to the application that is allowed tocomplete the spooling of the print job successfully.

Determining the State of a Physical Printer

There is one fundamental premise that must be true to determine the stateof a physical printer: the Spooler must be attempting to send a print jobto the physical printer. This is the only time the state of the printer isreported by the port monitor. In addition, the most meaningful informationmay be reported in the status members of a JOB_INFO structure for that particular print job because some port monitor will have set these values directly.

The JOB_INFO structures contain a Status member and a pStatus member. Both members contain status information of a print job reported by the port monitor. These two members differ in that the Status member is a bit field of states that contains predetermined values, while the pStatus member is a pointer to a string that could contain just about anything. These values are documented by the Win32 SDK and the WinSpool.h header file. The pStatus member is sometimes, but not always, set to a descriptive status string. The contents of this string are defined by each port monitor.

JOB_INFO structures are returned by two API functions: GetJob and EnumJobs. EnumJobs returns an array of JOB_INFO structures without requiring that the caller reference a particular job in the Printer queue. The print job that is currently despooling (printing) contains the status information. To find this job in the array, search the array of JOB_INFO structures to locate the print job whose Status member has the JOB_STATUS_PRINTING bit set.

An easier method of determining the printer status is to examine the Statusmember of a PRINTER_INFO structure. This structure is returned by the GetPrinter function. There is a disadvantage to this approach in that there is no pStatus string member in a PRINTER_INFO structure that might provide more detailed or extensive state information. However, there is an advantage in that a port monitor may set some of the more extensive printer status bits of the PRINTER_INFO structure. Note, however, that the default port monitor for Windows does not usually set more than the PRINTER_STATUS_ERROR bit of a Printer's Status member.

Note that the Status members of either set of structures may contain stateinformation that is not strictly related to the physical printer. Forexample, the Status member of the PRINTER_INFO structures may be set with PRINTER_STATUS_PAUSED or PRINTER_STATUS_PENDING_DELETION, that are strictly relevant to the Print Queue. Also, the Status member of the JOB_INFO structure may contain state values for JOB_STATUS_PAUSED or JOB_STATUS_DELETING, that are relevant only to that particular print job. Note, also, that print jobs may accumulate in a Print queue after they have despooled and would be left with a state of JOB_STATUS_PRINTED.

Each of these functions requires a handle to a printer to identify thedesired Printer. This handle is obtained from the OpenPrinter function, that accepts a string containing the name of the printer. This name can be either the local name of the printer or a UNC share name to a network printer.

The following sample code demonstrates how to call the EnumJobs function properly to retrieve JOB_INFO structures and how to call the GetPrinter function to retrieve PRINTER_INFO structures:

Sample Code

   BOOL GetJobs(HANDLE hPrinter,        /* Handle to the printer. */                 JOB_INFO_2 **ppJobInfo, /* Pointer to be filled.  */                 int *pcJobs,            /* Count of jobs filled.  */                 DWORD *pStatus)         /* Print Queue status.    */    {   DWORD               cByteNeeded,                        nReturned,                        cByteUsed;    JOB_INFO_2          *pJobStorage = NULL;    PRINTER_INFO_2       *pPrinterInfo = NULL;   /* Get the buffer size needed. */        if (!GetPrinter(hPrinter, 2, NULL, 0, &cByteNeeded))       {           if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)               return FALSE;       }       pPrinterInfo = (PRINTER_INFO_2 *)malloc(cByteNeeded);       if (!(pPrinterInfo))           /* Failure to allocate memory. */            return FALSE;       /* Get the printer information. */        if (!GetPrinter(hPrinter,               2,               (LPSTR)pPrinterInfo,               cByteNeeded,               &cByteUsed))       {           /* Failure to access the printer. */            free(pPrinterInfo);           pPrinterInfo = NULL;           return FALSE;       }       /* Get job storage space. */        if (!EnumJobs(hPrinter,               0,               pPrinterInfo->cJobs,               2,               NULL,               0,               (LPDWORD)&cByteNeeded,               (LPDWORD)&nReturned))       {           if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)           {               free(pPrinterInfo);               pPrinterInfo = NULL;               return FALSE;           }       }       pJobStorage = (JOB_INFO_2 *)malloc(cByteNeeded);       if (!pJobStorage)       {           /* Failure to allocate Job storage space. */            free(pPrinterInfo);           pPrinterInfo = NULL;           return FALSE;       }       ZeroMemory(pJobStorage, cByteNeeded);       /* Get the list of jobs. */        if (!EnumJobs(hPrinter,               0,               pPrinterInfo->cJobs,               2,               (LPBYTE)pJobStorage,               cByteNeeded,               (LPDWORD)&cByteUsed,               (LPDWORD)&nReturned))       {           free(pPrinterInfo);           free(pJobStorage);           pJobStorage = NULL;           pPrinterInfo = NULL;           return FALSE;       }       /*        *  Return the information.        */        *pcJobs = nReturned;       *pStatus = pPrinterInfo->Status;       *ppJobInfo = pJobStorage;       free(pPrinterInfo);       return TRUE;   }   BOOL IsPrinterError(HANDLE hPrinter)   {       JOB_INFO_2  *pJobs;       int         cJobs,                   i;       DWORD       dwPrinterStatus;       /*        *  Get the state information for the Printer Queue and        *  the jobs in the Printer Queue.        */        if (!GetJobs(hPrinter, &pJobs, &cJobs, &dwPrinterStatus))			return FALSE;       /*        *  If the Printer reports an error, believe it.        */        if (dwPrinterStatus &           (PRINTER_STATUS_ERROR |           PRINTER_STATUS_PAPER_JAM |           PRINTER_STATUS_PAPER_OUT |           PRINTER_STATUS_PAPER_PROBLEM |           PRINTER_STATUS_OUTPUT_BIN_FULL |           PRINTER_STATUS_NOT_AVAILABLE |           PRINTER_STATUS_NO_TONER |           PRINTER_STATUS_OUT_OF_MEMORY |           PRINTER_STATUS_OFFLINE |           PRINTER_STATUS_DOOR_OPEN))       {           free( pJobs );           return TRUE;       }       /*        *  Find the Job in the Queue that is printing.        */        for (i=0; i < cJobs; i++)       {           if (pJobs[i].Status & JOB_STATUS_PRINTING)           {               /*                *  If the job is in an error state,                *  report an error for the printer.                *  Code could be inserted here to                *  attempt an interpretation of the                *  pStatus member as well.                */                if (pJobs[i].Status &                   (JOB_STATUS_ERROR |                   JOB_STATUS_OFFLINE |                   JOB_STATUS_PAPEROUT |                   JOB_STATUS_BLOCKED_DEVQ))               {                   free( pJobs );                   return TRUE;               }           }       }       /*        *  No error condition.        */        free( pJobs );       return FALSE;   }				
NOTE: When printer pooling is enabled on Windows NT, there may be more than one print job despooling from a Printer queue that will report a status. This sample code does not consider that circumstance.
For additional information about general instructions on calling Win32 Spooler functions, click the following article number to view the article in the Microsoft Knowledge Base:
158828 How To Call Wind32 Spooler enumeration APIs properly

Article ID: 160129 - Last Review: 06/22/2014 18:11:00 - Revision: 6.0

Microsoft Win32 Application Programming Interface

  • kbFAQ kbhowto KB160129