Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

EnumJobs, JOB_INFO_2, количество страниц отправленных напечать

394
10 января 2011 года
MegaMozg
317 / / 18.03.2006
Приветствую. Я снова о принтерах :D
С помощью EnumJobs() данные о задании принтера JOB_INFO_2. Все данные верные, а количество страниц (TotalPages) всегда показывает равным 0.
В MSDN написано:
Цитата:

TotalPages
Specifies the number of pages required for the job. This value may be zero if the print job does not contain page delimiting information.


Ладно пусть там будет ноль, но стандартный виндовый диспетчер всегда знает количество страниц в задании.
как тогда узнать количество страниц?

535
10 января 2011 года
Нездешний
537 / / 17.01.2008
:D может, вот это поможет:
Цитата:
Found it!!

On the client side, on devices and printers, right-click on any printer, and select printer properties, and click the Sharing tab. Tick off "Render print jobs on client computers". This will cause the events to fire on the server spool. If you're running server 2008 you can do this on the server as well and it will affect all client jobs.

To set programatically or for more info:
http://msdn.microsoft.com/en-us/library/ff556443.aspx

394
10 января 2011 года
MegaMozg
317 / / 18.03.2006
Не помогло пока
342
11 января 2011 года
Yos
209 / / 21.06.2003
Делается (и выводится) это так
 
Код:
wsprintf(value,"%d/%d",info.PagesPrinted,info.PagesPrinted+info.TotalPages);

т.е. если все страницы уже попали в память принтера то info.TotalPages - всегда будет равно 0, а вот если задание будет листов так на 1000 то память все и сразу принять не сможет, вот тогда там будет отличное от 0 значение...
394
11 января 2011 года
MegaMozg
317 / / 18.03.2006
Цитата: Yos

т.е. если все страницы уже попали в память принтера то info.TotalPages - всегда будет равно 0,


сомневаюсь, с чего бы обнулять это поле?

Перед получением информации задание приостанавливается, по этому PagesPrinted тоже равно 0

342
11 января 2011 года
Yos
209 / / 21.06.2003
2 года назад писал свою реализацию менеджера печати для одного банковского проекта (приведенная строка оттуда), где стандартные средства не доступны. Так вот это делается именно так, и никак иначе, причем уже очень давно и не зависит от версии виндов.

Практика и исходники 2000-ка, которые мне однажды удалось посмотреть, к то му же это подтверждают. + если задание приостанавливается, то приостанавливается весь процесс обработки задания, там вообще ничего меняться не будет...
342
11 января 2011 года
Yos
209 / / 21.06.2003
Почему так (теория и практика):

Входные данные:
1. Буфер печати принтера.
2. Очередь сервера печати.
3. Приложение, которое печатает.

Работа:
Буфер чист, Очередь чиста. Приложение начинает печатать - StartDoc(hdc,&document);
Буфер чист, Очередь инициализируется.
Приложение - StartPage(hdc);
Очередь получает данные
Приложение - EndPage(hdc);
Страница помещается в буфер - PagesPrinted+1
Приложение - StartPage(hdc);
Очередь получает данные
Приложение - EndPage(hdc);
Страница помещается в буфер - PagesPrinted+1
Приложение - StartPage(hdc);
Очередь получает данные
Приложение - EndPage(hdc);
Страница помещается в буфер но буфер полон - TotalPages+1 - остается в очереди
Приложение - StartPage(hdc);
Очередь получает данные
Приложение - EndPage(hdc);
В очереди еще есть данные - TotalPages+1 - остается в очереди
...
Буфер освобождается
Страница помещается в буфер - PagesPrinted+1,TotalPages-1
...
Приложение заканчивает печать - EndDoc(hdc);
Общее количество страниц - PagesPrinted+TotalPages...

Т.е. оно будет известно только тогда, когда закончится помещение задания в очередь...
394
11 января 2011 года
MegaMozg
317 / / 18.03.2006
У меня в TotalPages изначально 0, похоже. MSDN говорит, что так может быть (см. мой первый пост). Какие еще есть способы узнать количество страниц кроме JOB_INFO_x?

Вот код который мониторит принтер и приостанавливает задание, может тут, что не так делаю... (подробные комментарии на русском - желание заказчика:))

Код:
unsigned _stdcall monitorThread(void *pParam)
{
    PRINTER_INFO_2 prninfo = *(PRINTER_INFO_2*)pParam;      // копируем информацию о притнтере
    HANDLE hPrinter;                                        // дескриптор принтера
    HANDLE chObj;                                           // дескриптор события-изменения
    DWORD pdwChange;                                        // тип события-изменения
    DWORD dwNeeded;                                         // необходимо зарезервировать для массива задач
        DWORD dwReturned;                                       // количество задач
    JOB_INFO_2* pJobInfo;                                   // указатель на массив задач принтера

        DWORD blah;
        PRINTER_INFO_2 pin2;

    // открываем принтер
    if(!OpenPrinter(prninfo.pPrinterName, &hPrinter, NULL))
                // если возникла ошибка при открытии
        MessageBox(0, "Printer not open", "Error", MB_OK);
   
        // ------ "мониторим" изменения заданий принтера ----- //
    chObj = FindFirstPrinterChangeNotification(hPrinter, PRINTER_CHANGE_JOB, 0, NULL);
    if (chObj != INVALID_HANDLE_VALUE)
    {
        while (true)
        {
                        // ждем уведомления об изменении
            WaitForSingleObject(chObj, INFINITE);
                        // принимаем изменение
            BOOL fcnreturn = FindNextPrinterChangeNotification(chObj, &pdwChange, NULL, NULL);
            if (fcnreturn)
            {
                    // если изменение это добавление нового задания, то...
                if(pdwChange == PRINTER_CHANGE_ADD_JOB)
                {

                    // получаем список задач
                    EnumJobs( hPrinter, 0, 0xFFFFFFFF, 2, NULL, 0, &dwNeeded,&dwReturned );
                    pJobInfo = new JOB_INFO_2[dwNeeded];
                    if(!EnumJobs( hPrinter, 0, 0xFFFFFFFF, 2, (LPBYTE)pJobInfo,dwNeeded, &dwNeeded, &dwReturned) )
                                                // если возникла ошибка
                                        MessageBox(0, "Monitoring fail", "Error", MB_OK);
                    // --- выбираем последнюю задачу и добавляем в нашу очередь --- //
                                        // ждем пока освободится доступ к g_Jobs и захватываем мьютекс
                    WaitForSingleObject(g_h[0], INFINITE);
                    // приостанавливаем последнюю задачу
                                        SetJob(hPrinter, pJobInfo[dwReturned - 1].JobId, 2, (LPBYTE)&pJobInfo[dwReturned - 1], JOB_CONTROL_PAUSE);
                    // добавляем в очередь
                    g_Jobs.push(pJobInfo[dwReturned - 1]);
                                        // изменяем состаяние семафора
                    ReleaseSemaphore(g_h[1], 1, NULL);
                                        // освобождаем мьютекс
                    ReleaseMutex(g_h[0]);

                }

            }

        }
        }
    else
    {
                // если возникла ошибка
                MessageBox(0, "Monitoring fail", "Error", MB_OK);
    }

    // закрываем описатели и освобождаем память
    FindClosePrinterChangeNotification(chObj);
        ClosePrinter(hPrinter);
        delete[] pJobInfo;

    return 0;
}
342
11 января 2011 года
Yos
209 / / 21.06.2003
Ну начнем с того, что приостанавливать задание нет никакого смысла, так как возвращаемые данные EnumJobs в вашем "статическом" буфере и это никак не влияет на процесс печати, но зато тормозит задания, причем четко, а если сюда добавить, что вы не возобновляете его после добавления, то это ахтунг... Тем более после этого чего должен ожидать WaitForSingleObject(chObj,INFINITE); - он так никогда и не выйдет из стопора...

К чему это все приводит, сейчас затрудняюсь ответить предметно, не проверял... Но вполне может быть что и к некорректному отображению инфы по заданию(ям) - онож стоит...

У меня была другая реализация - привязка моей очереди не через добавление только нового, а анализ существующих через .JobId. Т.е. если такой есть, обновляем данные, если нет, добавляем новое задание. А если в моей есть а в выданных нет - то удаляем:

Код:
//*************************************************************
//*
//* Поток процесса
//*
//*************************************************************
void TJobsUpdater::ExecuteThreadProcess(void)
{
 //****
 HANDLE             hPrinter;

 JOB_INFO_2         info[MAX_ITEM_COUNT];

 CHAR               value[MAX_TEXT_SIZE];

 DWORD              size;
 DWORD              count;

 DWORD              row;
 DWORD              id;

 DWORD              i;

 BOOL               search;

 SYSTEMTIME         time;
 FILETIME           system;
 FILETIME           local;

 // если есть отображаемый список
 if( pJobsList )
 {
    // открываем принтер
    if( OpenPrinter(pFrame->PrinterPath,&hPrinter,NULL) )
    {
        // получаем список заданий печати
        if( EnumJobs(hPrinter,0,MAX_ITEM_COUNT,2,(LPBYTE)info,MAX_ITEM_COUNT*sizeof(JOB_INFO_2),&(size=0),&(count=0)) )
        {
            // выводим информацию о количестве заданий печати
            if( count > 0 ) wsprintf(value,"Количество заданий: %d",count);
            else wsprintf(value,"Нет заданий на печать");
            pStatBar->SetItemName(value,1);

            // проходимся по всем строкам
            for( row=0; pJobsList->GetRowID(row,&id); row++ )
            {
                // сбрасываем признак существования элемента
                search = FALSE;

                // проходимся по всем заданиям на печать
                for( i=0; i<count; i++ )
                {
                    // если нашли элемент
                    if( id == info.JobId )
                    {
                        // устанавливаем признак существования элемента
                        search = TRUE;
                        break;
                    }
                }

                // если элемента нет
                if( !search )
                {
                    // удаляем элемент и корректируем номер
                    // так как после удаления происходит сдвижка строк
                    if( pJobsList->DeleteRow(row) ) row -= 1;
                }
            }

            // проходимся по всем заданиям на печать
            for( i=0; i<count; i++ )
            {
                // добавляем строку
                if( pJobsList->AppendRow(0,info.JobId,&row) )
                {
                    // выводим название документа
                    pJobsList->SetValue(0,row,info.pDocument);
                   
                    // если идет печать
                    if( info.Status & JOB_STATUS_PRINTING )
                    {
                        // если ошибка
                        if( info.Status & (JOB_STATUS_ERROR | JOB_STATUS_OFFLINE | JOB_STATUS_PAPEROUT | JOB_STATUS_BLOCKED_DEVQ) ) wsprintf(value,"Ошибка - Идет печать");
                        // все нормально
                        else wsprintf(value,"Идет печать");
                    }

                    // просто очередь
                    else
                    {
                        // анализируем
                        if( info.Status )
                        {
                            if( info.Status & JOB_STATUS_BLOCKED_DEVQ ) wsprintf(value,"Документ не может быть напечатан"/*"The driver cannot print the job"*/);
                            if( info.Status & JOB_STATUS_COMPLETE ) wsprintf(value,"Ожидание"/*"Job is sent to the printer, but the job may not be printed yet"*/);
                            if( info.Status & JOB_STATUS_DELETED ) wsprintf(value,"Удалено"/*"Job has been deleted"*/);
                            if( info.Status & JOB_STATUS_DELETING ) wsprintf(value,"Удаление"/*"Job is being deleted"*/);
                            if( info.Status & JOB_STATUS_ERROR ) wsprintf(value,"Ошибка"/*"An error is associated with the job"*/);
                            if( info.Status & JOB_STATUS_OFFLINE ) wsprintf(value,"Принтер выключен"/*"Printer is offline"*/);
                            if( info.Status & JOB_STATUS_PAPEROUT ) wsprintf(value,"Нет бумаги"/*"Printer is out of paper"*/);
                            if( info.Status & JOB_STATUS_PAUSED ) wsprintf(value,"Пауза"/*"Job is paused"*/);
                            if( info.Status & JOB_STATUS_PRINTED ) wsprintf(value,"Готово"/*"Job has printed"*/);
                            if( info.Status & JOB_STATUS_PRINTING ) wsprintf(value,"Идет печать"/*"Job is printing"*/);
                            if( info.Status & JOB_STATUS_RESTART ) wsprintf(value,"Перезапуск"/*"Job has been restarted"*/);
                            if( info.Status & JOB_STATUS_SPOOLING ) wsprintf(value,"Идет печать"/*"Job is spooling"*/);
                            if( info.Status & JOB_STATUS_USER_INTERVENTION ) wsprintf(value,"Ожидание действий пользователя"/*"Printer has an error that requires the user to do something"*/);
                        }
                        else wsprintf(value,"");
                    }

                    // выводим состояние
                    pJobsList->SetValue(1,row,value);

                    // выводим пользователя
                    pJobsList->SetValue(2,row,info.pUserName);
                   
                    // выводим печатаемые страницы
                    wsprintf(value,"%d/%d",info.PagesPrinted,info.PagesPrinted+info.TotalPages);
                    pJobsList->SetValue(3,row,value);
                   
                    // выводим время постановки в очередь
                    SystemTimeToFileTime(&info.Submitted,&system);
                    FileTimeToLocalFileTime(&system,&local);
                    FileTimeToSystemTime(&local,&time);
                    wsprintf(value,"%02d.%02d.%04d в %02d:%02d:%02d",time.wDay,time.wMonth,time.wYear,time.wHour,time.wMinute,time.wSecond);
                    pJobsList->SetValue(4,row,value);
                }
            }
        }

        // нет заданий печати - удаляем все задания
        if( !count ) pJobsList->DeleteAllRows();

        // закрываем принтер
        ClosePrinter(hPrinter);
    }
 }

 // ожидаем 1 секунду - некорректно, но раз никто не жалуется - оставляем пока так
 Sleep(1000);
}


PS В MSDN кстати много неточностей и недсказанностей, особенно если там указано что в 2000 так же как и последующих версиях...

PPS А коментарии к коду это правильно без всяких там заказчиков. У меня в рабочих проектах как правило их в два раза больше чем самого кода. При этом кроме меня исходники никто больше не видит, зато если необходимо вернуться (типа выпустить патч) через пару месяцев или лет, то процесс "вспоминания что я там делал" не является проблемой...
394
11 января 2011 года
MegaMozg
317 / / 18.03.2006
Цитата: Yos
Ну начнем с того, что приостанавливать задание нет никакого смысла, так как возвращаемые данные EnumJobs в вашем "статическом" буфере и это никак не влияет на процесс печати, но зато тормозит задания, причем четко, а если сюда добавить, что вы не возобновляете его после добавления, то это ахтунг... Тем более после этого чего должен ожидать WaitForSingleObject(chObj,INFINITE); - он так никогда и не выйдет из стопора...



Программа должна отловить и приостановить задание, затем вывести информацию о нем (в том числе и количество страниц), юзер жмет одну из кнопок: продолжить или отменить задание.

реализовано у меня это так:
1. "перечисляем" принтеры EnumPrinters
2. для каждого принтера запускаю, приведенный выше, monitorThread
3. "монитор" останавливает задания и инфу отправляет в мою очередь
4. есть один поток, который, если моя очередь не пуста, выбирает из нее задания, по одному, и отображает инфу
5. юзер "продолжает" или "отменяет" задание, поток даем ему еще задание, если есть

342
12 января 2011 года
Yos
209 / / 21.06.2003
Так, все понятно, но есть одно но, о котором я писал ранее - PRINTER_CHANGE_ADD_JOB будет тогда, когда StartDoc. А при этом везде будут 0 страниц.

По этому надо делать следующее - анализировать изменение задания (проверил, приостановка - это приостановка печати, а не постановки в очередь) и динамически менять состояние о задании, которое дается пользователю. Так как количество страниц будет известно только тогда, когда все задание будет поставлено в очередь и закрыто EndDoc.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог