Практическое руководство. Диагностика проблем при выполнении заданий печати
Обновлен: Ноябрь 2007
Часто администраторы сети получают жалобы от пользователей по поводу заданий печати, которые не выполняются или выполняются медленно. В интерфейсе API-интерфейсыMicrosoft .NET Framework представлен широкий набор свойств заданий печати, обеспечивающих быструю удаленную диагностику заданий печати.
Пример
Ниже приведены основные этапы по созданию служебной программы такого рода.
Идентификация задания печати, на которое жалуется пользователь. Часто пользователи не могут сделать это точно. Они могут не знать имена серверов печати или принтеров. Пользователи могут описывать расположение принтера в терминологии отличной от той, которая была использована при установке свойства Location. В связи с этим рекомендуется создавать список текущих заданий, принятых от пользователей. При наличии нескольких заданий выявление проблемного задания может осуществляться во взаимодействии пользователя и администратора системы печати. Ниже приведены соответствующие промежуточные этапы.
Получите список всех серверов печати.
Выполните цикл опроса серверов об их очередях печати.
На каждом шаге цикла выполните цикл опроса заданий во всех очередях сервера.
На каждом шаге цикла опроса очередей выполните цикл опроса всех заданий и соберите идентифицирующие сведения о заданиях, отправленных жалующимся пользователем.
При выявлении проблемного задания печати просмотрите соответствующие свойства для определения источника проблемы. Например, задание может находиться в состоянии ошибки, или перед печатью задания выключен принтер, обслуживающий очередь.
В приведенном ниже коде представлен ряд примеров. Полный пример см. в разделе Пример диагностики неполадок при выполнении заданий печати.
В первом примере кода представлен цикл опроса очередей печати. (Шаг «1в» выше.) Переменная myPrintQueues представляет собой объект класса PrintQueueCollection для текущего сервера печати.
Пример кода начинается с обновления текущего объекта очереди печати с помощью метода PrintQueue.Refresh. Это гарантирует точное представление состояния соответствующего физического принтера с помощью свойств объекта. Затем в приложении извлекается текущая коллекцию заданий печати в очереди с помощью метода GetPrintJobInfoCollection.
После этого в приложении просматривается коллекция PrintSystemJobInfo, каждое свойство Submitter в которой сравнивается с псевдонимом жалующегося пользователя. Если они совпадают, идентифицирующие сведения о задании добавляются к соответствующей строке. (Переменные userName и jobList инициализируются в приложении ранее.)
foreach (PrintQueue pq in myPrintQueues)
{
pq.Refresh();
PrintJobInfoCollection jobs = pq.GetPrintJobInfoCollection();
foreach (PrintSystemJobInfo job in jobs)
{
// Since the user may not be able to articulate which job is problematic,
// present information about each job the user has submitted.
if (job.Submitter == userName)
{
atLeastOne = true;
jobList = jobList + "\nServer:" + line;
jobList = jobList + "\n\tQueue:" + pq.Name;
jobList = jobList + "\n\tLocation:" + pq.Location;
jobList = jobList + "\n\t\tJob: " + job.JobName + " ID: " + job.JobIdentifier;
}
}// end for each print job
}// end for each print queue
for each (PrintQueue^ pq in myPrintQueues)
{
pq->Refresh();
PrintJobInfoCollection^ jobs = pq->GetPrintJobInfoCollection();
for each (PrintSystemJobInfo^ job in jobs)
{
// Since the user may not be able to articulate which job is problematic,
// present information about each job the user has submitted.
if (job->Submitter == userName)
{
atLeastOne = true;
jobList = jobList + "\nServer:" + line;
jobList = jobList + "\n\tQueue:" + pq->Name;
jobList = jobList + "\n\tLocation:" + pq->Location;
jobList = jobList + "\n\t\tJob: " + job->JobName + " ID: " + job->JobIdentifier;
}
}
}
В следующем примере выбирается приложение на шаге 2 (см. выше). После определения проблемного задания в приложении запрашиваются сведения, которые будут идентифицировать это задание. На основании этих сведений создаются объекты PrintServer, PrintQueue и PrintSystemJobInfo.
На этом этапе в приложении содержится структура ветвления, соответствующая двум способам проверки состояния задачи печати:
чтение флагов свойства JobStatus, имеющего тип PrintJobStatus;
чтение каждого соответствующего свойства, например IsBlocked и IsInError.
В этом примере демонстрируются оба метода. Пользователь имеет возможность заранее выбрать используемый метод (чтобы использовать флаги свойства JobStatus, следует выбрать ответ «Y»). Ниже приведены сведения об этих двух методах. В завершение, в приложении используется метод ReportQueueAndJobAvailability для определения возможности печати задания в это время дня. Описание этого метода см. в разделе Практическое руководство. Определение возможности печати в заданное время суток.
// When the problematic print job has been identified, enter information about it.
Console.Write("\nEnter the print server hosting the job (including leading slashes \\\\): " +
"\n(press Return for the current computer \\\\{0}): ", Environment.MachineName);
String pServer = Console.ReadLine();
if (pServer == "")
{
pServer = "\\\\" +Environment.MachineName;
}
Console.Write("\nEnter the print queue hosting the job: ");
String pQueue = Console.ReadLine();
Console.Write("\nEnter the job ID: ");
Int16 jobID = Convert.ToInt16(Console.ReadLine());
// Create objects to represent the server, queue, and print job.
PrintServer hostingServer = new PrintServer(pServer, PrintSystemDesiredAccess.AdministrateServer);
PrintQueue hostingQueue = new PrintQueue(hostingServer, pQueue, PrintSystemDesiredAccess.AdministratePrinter);
PrintSystemJobInfo theJob = hostingQueue.GetJob(jobID);
if (useAttributesResponse == "Y")
{
TroubleSpotter.SpotTroubleUsingJobAttributes(theJob);
// TroubleSpotter class is defined in the complete example.
}
else
{
TroubleSpotter.SpotTroubleUsingProperties(theJob);
}
TroubleSpotter.ReportQueueAndJobAvailability(theJob);
// When the problematic print job has been identified, enter information about it.
Console::Write("\nEnter the print server hosting the job (including leading slashes \\\\): " + "\n(press Return for the current computer \\\\{0}): ", Environment::MachineName);
String^ pServer = Console::ReadLine();
if (pServer == "")
{
pServer = "\\\\" + Environment::MachineName;
}
Console::Write("\nEnter the print queue hosting the job: ");
String^ pQueue = Console::ReadLine();
Console::Write("\nEnter the job ID: ");
Int16 jobID = Convert::ToInt16(Console::ReadLine());
// Create objects to represent the server, queue, and print job.
PrintServer^ hostingServer = gcnew PrintServer(pServer, PrintSystemDesiredAccess::AdministrateServer);
PrintQueue^ hostingQueue = gcnew PrintQueue(hostingServer, pQueue, PrintSystemDesiredAccess::AdministratePrinter);
PrintSystemJobInfo^ theJob = hostingQueue->GetJob(jobID);
if (useAttributesResponse == "Y")
{
TroubleSpotter::SpotTroubleUsingJobAttributes(theJob);
// TroubleSpotter class is defined in the complete example.
} else
{
TroubleSpotter::SpotTroubleUsingProperties(theJob);
}
TroubleSpotter::ReportQueueAndJobAvailability(theJob);
Чтобы проверить состояние задания печати с помощью флагов свойства JobStatus, следует выполнить проверку состояния всех соответствующих флагов. В качестве стандартного способа проверки состояния бита в наборе битовых флагов используется логическая операция AND с набором флагов в качестве одного операнда и самого флага — в качестве другого. Так как сам флаг имеет только одно битовое значение, результатом применения логического оператора AND является то, что в большинстве случаев устанавливается тот же самый бит. Чтобы узнать, справедливо ли это, просто сравните результат применения логического оператора AND и значение самого флага. Дополнительные сведения см. в разделах PrintJobStatus, Оператор & (Справочник по C#) и FlagsAttribute.
Для каждого атрибута, для которого установлен этот бит, в окне консоли выводится соответствующее уведомление, а в некоторых случаях также предлагаются способы реагирования. (Метод HandlePausedJob, который вызывается в случае приостановки задания или очереди, описывается ниже.)
// Check for possible trouble states of a print job using the flags of the JobStatus property
internal static void SpotTroubleUsingJobAttributes(PrintSystemJobInfo theJob)
{
if ((theJob.JobStatus & PrintJobStatus.Blocked) == PrintJobStatus.Blocked)
{
Console.WriteLine("The job is blocked.");
}
if (((theJob.JobStatus & PrintJobStatus.Completed) == PrintJobStatus.Completed)
||
((theJob.JobStatus & PrintJobStatus.Printed) == PrintJobStatus.Printed))
{
Console.WriteLine("The job has finished. Have user recheck all output bins and be sure the correct printer is being checked.");
}
if (((theJob.JobStatus & PrintJobStatus.Deleted) == PrintJobStatus.Deleted)
||
((theJob.JobStatus & PrintJobStatus.Deleting) == PrintJobStatus.Deleting))
{
Console.WriteLine("The user or someone with administration rights to the queue has deleted the job. It must be resubmitted.");
}
if ((theJob.JobStatus & PrintJobStatus.Error) == PrintJobStatus.Error)
{
Console.WriteLine("The job has errored.");
}
if ((theJob.JobStatus & PrintJobStatus.Offline) == PrintJobStatus.Offline)
{
Console.WriteLine("The printer is offline. Have user put it online with printer front panel.");
}
if ((theJob.JobStatus & PrintJobStatus.PaperOut) == PrintJobStatus.PaperOut)
{
Console.WriteLine("The printer is out of paper of the size required by the job. Have user add paper.");
}
if (((theJob.JobStatus & PrintJobStatus.Paused) == PrintJobStatus.Paused)
||
((theJob.HostingPrintQueue.QueueStatus & PrintQueueStatus.Paused) == PrintQueueStatus.Paused))
{
HandlePausedJob(theJob);
//HandlePausedJob is defined in the complete example.
}
if ((theJob.JobStatus & PrintJobStatus.Printing) == PrintJobStatus.Printing)
{
Console.WriteLine("The job is printing now.");
}
if ((theJob.JobStatus & PrintJobStatus.Spooling) == PrintJobStatus.Spooling)
{
Console.WriteLine("The job is spooling now.");
}
if ((theJob.JobStatus & PrintJobStatus.UserIntervention) == PrintJobStatus.UserIntervention)
{
Console.WriteLine("The printer needs human intervention.");
}
}//end SpotTroubleUsingJobAttributes
// Check for possible trouble states of a print job using the flags of the JobStatus property
static void SpotTroubleUsingJobAttributes (PrintSystemJobInfo^ theJob)
{
if ((theJob->JobStatus & PrintJobStatus::Blocked) == PrintJobStatus::Blocked)
{
Console::WriteLine("The job is blocked.");
}
if (((theJob->JobStatus & PrintJobStatus::Completed) == PrintJobStatus::Completed)
||
((theJob->JobStatus & PrintJobStatus::Printed) == PrintJobStatus::Printed))
{
Console::WriteLine("The job has finished. Have user recheck all output bins and be sure the correct printer is being checked.");
}
if (((theJob->JobStatus & PrintJobStatus::Deleted) == PrintJobStatus::Deleted)
||
((theJob->JobStatus & PrintJobStatus::Deleting) == PrintJobStatus::Deleting))
{
Console::WriteLine("The user or someone with administration rights to the queue has deleted the job. It must be resubmitted.");
}
if ((theJob->JobStatus & PrintJobStatus::Error) == PrintJobStatus::Error)
{
Console::WriteLine("The job has errored.");
}
if ((theJob->JobStatus & PrintJobStatus::Offline) == PrintJobStatus::Offline)
{
Console::WriteLine("The printer is offline. Have user put it online with printer front panel.");
}
if ((theJob->JobStatus & PrintJobStatus::PaperOut) == PrintJobStatus::PaperOut)
{
Console::WriteLine("The printer is out of paper of the size required by the job. Have user add paper.");
}
if (((theJob->JobStatus & PrintJobStatus::Paused) == PrintJobStatus::Paused)
||
((theJob->HostingPrintQueue->QueueStatus & PrintQueueStatus::Paused) == PrintQueueStatus::Paused))
{
HandlePausedJob(theJob);
//HandlePausedJob is defined in the complete example.
}
if ((theJob->JobStatus & PrintJobStatus::Printing) == PrintJobStatus::Printing)
{
Console::WriteLine("The job is printing now.");
}
if ((theJob->JobStatus & PrintJobStatus::Spooling) == PrintJobStatus::Spooling)
{
Console::WriteLine("The job is spooling now.");
}
if ((theJob->JobStatus & PrintJobStatus::UserIntervention) == PrintJobStatus::UserIntervention)
{
Console::WriteLine("The printer needs human intervention.");
}
};
Чтобы проверить состояние задания печати с помощью отдельных свойств, следует считать соответствующие свойства и вывести уведомления и возможные способы реагирования для свойств со значениями true в окне консоли. (Метод HandlePausedJob, который вызывается в случае приостановки задания или очереди, описывается ниже.)
// Check for possible trouble states of a print job using its properties
internal static void SpotTroubleUsingProperties(PrintSystemJobInfo theJob)
{
if (theJob.IsBlocked)
{
Console.WriteLine("The job is blocked.");
}
if (theJob.IsCompleted || theJob.IsPrinted)
{
Console.WriteLine("The job has finished. Have user recheck all output bins and be sure the correct printer is being checked.");
}
if (theJob.IsDeleted || theJob.IsDeleting)
{
Console.WriteLine("The user or someone with administration rights to the queue has deleted the job. It must be resubmitted.");
}
if (theJob.IsInError)
{
Console.WriteLine("The job has errored.");
}
if (theJob.IsOffline)
{
Console.WriteLine("The printer is offline. Have user put it online with printer front panel.");
}
if (theJob.IsPaperOut)
{
Console.WriteLine("The printer is out of paper of the size required by the job. Have user add paper.");
}
if (theJob.IsPaused || theJob.HostingPrintQueue.IsPaused)
{
HandlePausedJob(theJob);
//HandlePausedJob is defined in the complete example.
}
if (theJob.IsPrinting)
{
Console.WriteLine("The job is printing now.");
}
if (theJob.IsSpooling)
{
Console.WriteLine("The job is spooling now.");
}
if (theJob.IsUserInterventionRequired)
{
Console.WriteLine("The printer needs human intervention.");
}
}//end SpotTroubleUsingProperties
// Check for possible trouble states of a print job using its properties
static void SpotTroubleUsingProperties (PrintSystemJobInfo^ theJob)
{
if (theJob->IsBlocked)
{
Console::WriteLine("The job is blocked.");
}
if (theJob->IsCompleted || theJob->IsPrinted)
{
Console::WriteLine("The job has finished. Have user recheck all output bins and be sure the correct printer is being checked.");
}
if (theJob->IsDeleted || theJob->IsDeleting)
{
Console::WriteLine("The user or someone with administration rights to the queue has deleted the job. It must be resubmitted.");
}
if (theJob->IsInError)
{
Console::WriteLine("The job has errored.");
}
if (theJob->IsOffline)
{
Console::WriteLine("The printer is offline. Have user put it online with printer front panel.");
}
if (theJob->IsPaperOut)
{
Console::WriteLine("The printer is out of paper of the size required by the job. Have user add paper.");
}
if (theJob->IsPaused || theJob->HostingPrintQueue->IsPaused)
{
HandlePausedJob(theJob);
//HandlePausedJob is defined in the complete example.
}
if (theJob->IsPrinting)
{
Console::WriteLine("The job is printing now.");
}
if (theJob->IsSpooling)
{
Console::WriteLine("The job is spooling now.");
}
if (theJob->IsUserInterventionRequired)
{
Console::WriteLine("The printer needs human intervention.");
}
};
Метод HandlePausedJob позволяет пользователю приложения удаленно возобновить приостановленные задания. Поскольку приостановка очереди печати может быть вызвана необходимостью, при выполнении метода сначала отображается запрос на продолжение печати. При выборе варианта «Y» вызывается метод PrintQueue.Resume.
После этого отображается запрос на автоматическое возобновление задания, приостановленного независимо от очереди печати. (Сравните значения свойств PrintQueue.IsPaused и PrintSystemJobInfo.IsPaused.) При выборе варианта «Y» вызывается метод PrintSystemJobInfo.Resume. В противном случае вызывается метод Cancel.
internal static void HandlePausedJob(PrintSystemJobInfo theJob)
{
// If there's no good reason for the queue to be paused, resume it and
// give user choice to resume or cancel the job.
Console.WriteLine("The user or someone with administrative rights to the queue" +
"\nhas paused the job or queue." +
"\nResume the queue? (Has no effect if queue is not paused.)" +
"\nEnter \"Y\" to resume, otherwise press return: ");
String resume = Console.ReadLine();
if (resume == "Y")
{
theJob.HostingPrintQueue.Resume();
// It is possible the job is also paused. Find out how the user wants to handle that.
Console.WriteLine("Does user want to resume print job or cancel it?" +
"\nEnter \"Y\" to resume (any other key cancels the print job): ");
String userDecision = Console.ReadLine();
if (userDecision == "Y")
{
theJob.Resume();
}
else
{
theJob.Cancel();
}
}//end if the queue should be resumed
}//end HandlePausedJob
static void HandlePausedJob (PrintSystemJobInfo^ theJob)
{
// If there's no good reason for the queue to be paused, resume it and
// give user choice to resume or cancel the job.
Console::WriteLine("The user or someone with administrative rights to the queue" + "\nhas paused the job or queue." + "\nResume the queue? (Has no effect if queue is not paused.)" + "\nEnter \"Y\" to resume, otherwise press return: ");
String^ resume = Console::ReadLine();
if (resume == "Y")
{
theJob->HostingPrintQueue->Resume();
// It is possible the job is also paused. Find out how the user wants to handle that.
Console::WriteLine("Does user want to resume print job or cancel it?" + "\nEnter \"Y\" to resume (any other key cancels the print job): ");
String^ userDecision = Console::ReadLine();
if (userDecision == "Y")
{
theJob->Resume();
} else
{
theJob->Cancel();
}
}
};
См. также
Основные понятия
Документы в Windows Presentation Foundation