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 is
not despooling and reports no state information, the printer is considered
to be ready and idle.
MORE INFORMATION
As referred to by the Win32 API, a "printer" is comprised of the printer driver...
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 is 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 is the job
of the port monitor to report those errors.
The Spooler does not 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 is 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 cannot 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 is not
considered an error reportable to the application that is allowed to
complete 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 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. 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 state
information that is not 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. 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 the
desired 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.