取得印表機及列印工作的狀態

本文說明如何使用 Win32 多工緩衝處理程式來取得印表機及列印工作的狀態。

原始產品版本:  Win32 幕後列印程式
原始 KB 編號:  160129

在列印工作的 despool 期間,Win32 幕後列印程式會更新印表機和列印工作的狀態。 在所有其他時間,當該印表機未 despooling,而且報告沒有任何狀態資訊時,會將印表機視為已就緒且空閒。

如 WIN32 API 所參照,印表機是由印表機驅動程式、列印佇列及實體印表機的輸入/輸出路徑所組成。 作業系統只會將實體印表機視為一種列印工作的目的地,並透過系統印表機來傳遞,這篇文章的其餘部分稱為印表機。

印表機最明顯的部分是列印佇列。 它是由 Windows 95 樣式使用者介面中的列印管理員或印表機資料夾所管理。 印表機驅動程式是應用程式用來透過印表機 DCs 建立列印工作的印表機介面。 印表機的 I/O 路徑包含數個系統程式碼 culminating 與埠監視器。

埠監視器是實體印表機的介面,其位於系統印表機的下層端,負責傳送列印工作的資料,而不管實體印表機存在的連接。 在雙向列印機的情況下,埠監視器會負責將資料傳送到實體印表機及從該印表機傳送資料。 這種連線和實體印表機的錯誤發生的位置。 這是埠監視器的工作,可報告這些錯誤。

幕後列印程式不會查詢印表機連接之實體印表機的狀態。 相反地,實體印表機的狀態會決定當列印工作 despooled 透過埠監視器時,是否成功。 在此程式中發生錯誤時,埠監視器會報告錯誤,並記錄在列印工作的狀態資訊中。 幕後列印程式又會將合理的錯誤資訊傳播至印表機佇列。

因此,當印表機佇列空白時,系統印表機會報告無狀態。 在此狀態下,會假設已準備好接受列印工作的印表機。 即使實體印表機的錯誤狀態如離線,這也是有效的假設。 作業系統會認為印表機已準備好接受列印工作,即使由於某些原因,它無法完成傳遞至實體印表機。 這類情況被視為作業系統必須由使用者所解決的錯誤狀態。 它不會被視為可報告給應用程式的錯誤,允許成功完成列印工作的幕後列印。

決定實體印表機的狀態

若要判斷實體印表機的狀態,必須為 true 的一個基本的內部部署:幕後列印程式必須嘗試將列印工作傳送至實體印表機。 這是由埠監視器報告印表機狀態的唯一時間。 此外,在特定列印工作結構的狀態成員中,可能會報告最有意義的資訊, JOB_INFO 因為某些埠監視器會直接設定這些值。

JOB_INFO結構包含 Status 成員和 pStatus 成員。 這兩個成員都包含埠監視器報告之列印工作的狀態資訊。 這兩個成員的不同之處在于, Status 成員是一個包含預先定義值之狀態的位欄位,而 pStatus member 是指向可以包含任何專案的字串的指標。 這些值是由 Win32 SDK 和 WinSpool 標頭檔案所記錄。 pStatus成員有時不一定會設定為描述性狀態字串。 這個字串的內容是由每個埠監視器定義。

JOB_INFO結構是由兩個 API 函數傳回: GetJobEnumJobsEnumJobs傳回結構陣列, JOB_INFO 但不需要來電者參照印表機佇列中的特定工作。 目前 despooling (列印) 中的列印工作包含狀態資訊。 若要在陣列中尋找這項工作,請搜尋 JOB_INFO 結構陣列,以找出其 Status 成員具有位集的列印工作 JOB_STATUS_PRINTING

判斷印表機狀態的更簡單方法,就是檢查 Status 結構的成員 PRINTER_INFO 。 此函數會傳回此結構 GetPrinter 。 此方法的缺點是, pStatus 結構中沒有 PRINTER_INFO 可能會提供更多詳細資訊或大量狀態資訊的字串成員。 不過,您可以使用埠監視器來設定某些較廣泛的結構的印表機狀態位 PRINTER_INFO 。 不過,Windows 的預設埠監視器不會設定為超過 PRINTER_STATUS_ERROR 印表機成員的位 Status

注意

Status任何一組結構的成員可能都包含與實體印表機不嚴格相關的狀態資訊。 例如,可以 Status PRINTER_INFO 使用或設定與 PRINTER_STATUS_PAUSED PRINTER_STATUS_PENDING_DELETION 列印佇列嚴格相關的結構成員。 此外, Status 結構的成員 JOB_INFO 可能會包含或的狀態值 JOB_STATUS_PAUSED JOB_STATUS_DELETING ,只適用于特定的列印工作。 列印工作可以在 despooled 後累計列印佇列中,而且會保留狀態 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 上啟用印表機 pooling 時,可能會有多個列印工作 despooling 從將報告狀態的印表機佇列。 此範例程式碼不會考慮這種情況。