Получение состояния принтера и задания печати

В этой статье описывается, как получить состояние принтера и задания печати с помощью средства spooler Win32.

Исходная версия продукта: Диспетчер очереди Win32
Исходный номер базы знаний: 160129

Состояние принтеров и заданий печати обновляется диспетчером очереди Win32 во время десполи задания печати. В остальных случаях, когда этот принтер не десполичен и не сообщает сведения о состоянии, принтер считается готовым и бездействующим.

Как упоминалось в API Win32, принтер состоит из драйвера принтера, очереди печати и пути ввода-вывода к физическому принтеру. Операционная система рассматривает физический принтер как просто назначение задания печати, созданного и переданного через системный принтер, который в остальной части этой статьи называется принтером.

Наиболее видимой частью принтера является очередь печати. Он управляется диспетчером печати или папками принтеров в пользовательских интерфейсах Windows 95. Драйвер принтера — это интерфейс принтера, используемый приложениями для создания заданий печати с помощью контроллеров домена принтера. Путь ввода-вывода для принтера состоит из нескольких уровней системного кода, кульминацией которого является монитор портов.

Монитор портов — это интерфейс физического принтера на нижнем конце системного принтера и отвечает за передачу данных задания печати через любое существующее подключение к физическому принтеру. В случае двунаправленных принтеров монитор портов будет отвечать за передачу данных на физический принтер и из нее. Это подключение и физический принтер являются местом возникновения ошибок. Это задача монитора портов, чтобы сообщить об этих ошибках.

Диспетчер очереди не запрашивает состояние физического принтера, к которому подключен принтер. Вместо этого состояние физического принтера определяет успешность задания печати в момент его переноса на монитор порта. Если в этом процессе возникает какая-то ошибка, она сообщается монитором порта и записывается в сведения о состоянии задания печати. Диспетчер очереди, в свою очередь, распространяет разумные сведения об ошибках в очередь принтера.

Следовательно, системный принтер не сообщает о состоянии, если очередь принтера пуста. В этом состоянии предполагается, что принтер готов к приему заданий печати. Это допустимое предположение, даже если физический принтер находится в состоянии ошибки, например в автономном режиме. Операционная система считает принтер готовым к приему заданий печати, даже если по какой-либо причине он не может завершить доставку на физический принтер. Такое обстоятельство считается состоянием ошибки в операционной системе, которую должен устранить пользователь. Это не считается ошибкой, сообщаемой приложению, которому разрешено успешно завершить выполнение задания печати.

Определение состояния физического принтера

Существует одна основная предпосылка, которая должна быть верной для определения состояния физического принтера: диспетчер очереди должен пытаться отправить задание печати на физический принтер. Это единственный раз, когда монитор портов сообщает о состоянии принтера. Кроме того, наиболее значимая информация может быть сообщена в элементах состояния структуры для этого конкретного JOB_INFO задания печати, так как некоторые мониторы портов будут задавать эти значения напрямую.

Структуры JOB_INFO содержат Status элемент и pStatus элемент . Оба элемента содержат сведения о состоянии задания печати, сообщаемые монитором портов. Эти два элемента отличаются тем, что Status элемент — это битовое поле состояний, содержащее предопределенные значения, а pStatus член — это указатель на строку, которая может содержать что угодно. Эти значения задокументированы пакетом SDK для Win32 и файлом заголовка WinSpool.h. Иногда pStatus члену присваивается описательная строка состояния, но не всегда. Содержимое этой строки определяется каждым монитором портов.

JOB_INFO структуры возвращаются двумя функциями API: GetJob и EnumJobs. EnumJobs возвращает массив JOB_INFO структур, не требуя от вызывающего объекта ссылки на определенное задание в очереди принтера. Задание печати, которое в настоящее время десполируется (печать), содержит сведения о состоянии. Чтобы найти это задание в массиве, выполните поиск в массиве JOB_INFO структур, чтобы найти задание печати, член которого Status имеет JOB_STATUS_PRINTING набор битов.

Более простой способ определения состояния принтера — проверить Status член PRINTER_INFO структуры. Эта структура возвращается функцией GetPrinter . Недостаток этого подхода заключается в том, что в PRINTER_INFO структуре нет pStatus элемента строки, который мог бы предоставлять более подробные или подробные сведения о состоянии. Однако есть преимущество в том, что монитор портов может задать некоторые из более обширных бит состояния принтера в 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, которые относятся только к конкретному заданию печати. Задания печати могут накапливаться в очереди печати после того, как они деспуолированы и останутся с состоянием 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, в очереди принтеров может быть несколько заданий печати, которые будут сообщать о состоянии. Этот пример кода не учитывает это обстоятельство.