获取打印机和打印作业的状态

本文介绍如何使用 Win32 打印后台处理程序获取打印机和打印作业的状态。

原始产品版本: Win32 打印后台程序
原始 KB 数: 160129

打印机和打印作业的状态在打印作业出队期间由 Win32 后台处理程序更新。 在所有其他情况下,当该打印机未进行后台处理且不报告任何状态信息时,打印机将被视为准备就绪并处于空闲状态。

与 Win32 API 一样,打印机由打印机驱动程序、打印队列和物理打印机的输入/输出路径组成。 操作系统将物理打印机仅仅视为经过系统打印机生成并传递的打印作业的目标,本文其余部分称为打印机。

打印机最可见的部分是打印队列。 它由 Windows 95 样式用户界面中的打印管理器或打印机文件夹管理。 打印机驱动程序是应用程序用来通过打印机 DC 创建打印作业的打印机的接口。 打印机的 I/O 路径由多个系统代码层组成,最终使用端口监视器。

端口监视器是系统打印机下游端物理打印机的接口,负责通过与物理打印机的任何连接来传输打印作业的数据。 对于双向打印机,端口监视器负责在物理打印机之间传输和接收数据。 错误发生在连接和物理打印机处。 这是端口监视器的职责,报告这些错误。

打印后台处理程序不会查询与打印机连接的物理打印机的状态。 相反,物理打印机的状态决定了打印作业在通过端口监视器传输数据时是否成功。 如果在此过程中发生一些错误,端口监视器会报告该错误,并记录在打印作业的状态信息中。 后台处理程序(Spooler)反过来会将合理的错误信息传播到打印机队列。

因此,当打印机队列为空时,系统打印机不会报告任何状态。 在此状态下,假定打印机已准备好接受打印作业。 这是一个有效的假设,即使物理打印机处于错误状态(如离线)。 操作系统认为打印机已准备好接受打印作业,即使出于某种原因,它无法完成到物理打印机的交付。 这种情况被视为操作系统中必须由用户解决的错误状态。 不被视为可报告给应用程序的错误,它能够成功完成打印作业的后台处理。

确定物理打印机的状态

为了确定物理打印机的状态,有一个基本前提必须成立:后台打印程序必须尝试将打印作业发送到物理打印机。 这是端口监视器报告打印机状态的唯一时间。 此外,在某个特定打印作业的结构状态成员 JOB_INFO 中可能会报告最有意义的信息,因为某个端口监视器将直接设置这些值。

结构 JOB_INFO 包含成员 StatuspStatus 成员。 这两个成员都包含端口监视器报告的打印任务的状态信息。 这两个成员的不同之处在于 Status ,该成员是包含预先确定值的状态的位字段,而 pStatus 该成员是指向可以包含任何内容的字符串的指针。 这些值由 Win32 SDK 和 WinSpool.h 头文件记录。 成员 pStatus 有时(但并非总是)设置为描述性状态字符串。 此字符串的内容由每个端口监视器定义。

JOB_INFO 结构由两个 API 函数返回: GetJobEnumJobsEnumJobs 返回JOB_INFO 结构的数组,无需调用方引用打印机队列中的特定作业。 当前正在处理(打印)的打印作业包含状态信息。 若要在数组中找到此作业,请在结构数组JOB_INFO中搜索,以找到其Status成员已设置JOB_STATUS_PRINTING位的打印作业。

确定打印机状态的更简单方法是检查 Status 结构的成员 PRINTER_INFO 。 此结构由 GetPrinter 函数返回。 此方法有一个缺点,即在结构pStatus中,没有PRINTER_INFO字符串成员可以提供更详细或更广泛的状态信息。 然而,端口监视器在能够设置结构中一些更广泛的打印机状态位方面具有优势。 但是,Windows 的默认端口监视器不会设置的位数超过 PRINTER_STATUS_ERROR 打印机成员的 Status 位。

注意

Status这两组结构的成员可能包含与物理打印机不完全相关的状态信息。 例如,Status结构的PRINTER_INFO成员可以通过PRINTER_STATUS_PAUSEDPRINTER_STATUS_PENDING_DELETION进行设置,它们都与打印队列严格相关。 此外,Status结构的JOB_INFO成员可能包含与JOB_STATUS_PAUSEDJOB_STATUS_DELETING相关且仅与该特定打印作业相关的状态值。 打印作业可能会在打印队列中累积,打印完成后,其状态将显示为 JOB_STATUS_PRINTED

其中每个函数都需要一个打印机句柄来标识所需的打印机。 此句柄是从 OpenPrinter 函数获取的,该函数接受包含打印机名称的字符串。 此名称可以是打印机的本地名称,也可以是网络打印机的 UNC 共享名称。

以下示例代码演示如何正确调用 EnumJobs 函数以检索 JOB_INFO 结构,以及如何调用 GetPrinter 函数来检索 PRINTER_INFO 结构:

代码示例

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;

}

注意

在 Windows NT 上启用打印机池时,可能会有多个打印作业从打印机队列中输出并报告状态。 此示例代码不考虑这种情况。