Get the status of a printer and a print job

This article introduces how to get the status of a printer and a print job by using the Win32 Spooler.

Original product version:   Win32 Spooler
Original KB number:   160129

The status of printers and print jobs are updated by the Win32 Spooler during the despool of a print job. At all other times, when that printer isn't despooling and reports no state information, the printer is considered to be ready and idle.

As referred to by the Win32 API, a printer is comprised of the printer driver, the print queue, and the input/output path to the physical printer. The operating system treats a physical printer as merely the destination of a print job generated by and passed through a system Printer, referred to in the rest of this article as a Printer.

The most visible part of a Printer is a print queue. It's managed by the Print Manager or the Printer folders in the Windows 95-style user interfaces. The printer driver is the interface to the Printer that is used by applications to create print jobs via printer DCs. The I/O path for a Printer consists of several layers of system code culminating with a port monitor.

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 data of a print job across whatever connection exists to the physical printer. In the case of bi-directional printers, the port monitor would be responsible for transferring data to and from the physical printer. This connection, and the physical printer, are where errors occur. It's the job of the port monitor to report those errors.

The Spooler doesn't query for the state of a physical printer to which a Printer is connected. Instead, the state of a physical printer determines the success of a print job at the time it's despooled over the port monitor. If some error occurs in this process, the error is reported by the port 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 is empty. 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 error state such as off-line. The operating system considers the Printer ready to accept print jobs even if, for some reason, it can't complete delivery to the physical printer. Such a circumstance is considered an error state in the operating system that must be addressed by the user. It isn't considered an error reportable to the application that is allowed to complete the spooling of the print job successfully.

Determine the state of a physical printer

There is one fundamental premise that must be true to determine the state of a physical printer: the Spooler must be attempting to send a print job to the physical printer. This is the only time the state of the printer is reported by the port monitor. In addition, the most meaningful information may 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 Status member 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. However, that the default port monitor for Windows doesn't set more than the PRINTER_STATUS_ERROR bit of a Printer's Status member.

Note

The Status members of either set of structures may contain state information that isn't strictly related to the physical printer. For example, 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. 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 the wanted 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 doesn't consider that circumstance.