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

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

原始产品版本: Win32 后台处理程序
原始 KB 编号: 160129

打印机和打印作业的状态由 Win32 后台处理程序在打印作业的反池期间更新。 在所有其他时间,当打印机未进行反脱机且未报告任何状态信息时,打印机将被视为就绪且处于空闲状态。

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

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

端口监视器是系统打印机下流端物理打印机的接口,负责通过与物理打印机建立的任何连接传输打印作业的数据。 对于双向打印机,端口监视器将负责向物理打印机和从物理打印机传输数据。 此连接和物理打印机是发生错误的地方。 端口监视器的工作是报告这些错误。

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

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

确定物理打印机的状态

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

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

JOB_INFO 结构由两个 API 函数返回: GetJobEnumJobsEnumJobs 返回结构数组 JOB_INFO ,而无需调用方引用打印机队列中的特定作业。 当前正在取消后台处理 (打印) 的打印作业包含状态信息。 若要在数组中查找此作业,请搜索结构数组JOB_INFO以查找其成员具有位集的StatusJOB_STATUS_PRINTING打印作业。

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

注意

Status任一组结构的成员可能包含与物理打印机不完全相关的状态信息。 例如,Status可以使用 或 PRINTER_STATUS_PENDING_DELETION设置PRINTER_STATUS_PAUSED结构的成员PRINTER_INFO,这些成员与打印队列严格相关。 此外,Status结构的成员JOB_INFO可能包含 或 JOB_STATUS_DELETING的状态值,这些值JOB_STATUS_PAUSED仅与该特定打印作业相关。 打印作业在取消后台处理后可能会累积到打印队列中,并且将保留状态 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 上启用打印机池时,可能会有多个打印作业从打印机队列进行反池处理,这些作业将报告状态。 此示例代码不考虑这种情况。