Comment : diagnostiquer un travail d'impression problématique
Mise à jour : novembre 2007
Les administrateurs de réseaux reçoivent souvent des plaintes des utilisateurs à propos des travaux d'impression qui échouent ou sont trop lents. Les très nombreuses propriétés de travail d'impression proposées dans les API de Microsoft .NET Framework permettent d'effectuer rapidement un diagnostic à distance des travaux d'impression.
Exemple
Les étapes principales pour créer ce type d'utilitaire sont les suivantes :
Identifier le travail d'impression dont se plaint l'utilisateur. Souvent, les indications fournies par les utilisateurs ne sont pas très précises. Ils ne connaissant pas nécessairement les noms de leurs serveurs d'impression ou imprimantes. Ils peuvent décrire l'emplacement de l'imprimante avec une terminologie différente de celle utilisée dans la définition de la propriété Location. C'est pourquoi il est utile de générer une liste des travaux actuellement soumis par l'utilisateur. Si plusieurs travaux sont en attente d'impression, la communication entre l'utilisateur et l'administrateur du système d'impression peut permettre de localiser le travail problématique. Les sous-étapes sont les suivantes :
Obtenir une liste de tous les serveurs d'impression.
Parcourir les serveurs pour interroger leurs files d'attente à l'impression.
À chaque exploration du serveur, parcourir toutes les files d'attente de ce serveur pour interroger leurs travaux
À chaque exploration de la file d'attente, parcourir ses travaux et rassembler des indications sur ceux soumis par l'utilisateur plaignant.
Une fois le travail d'impression problématique identifié, examiner les propriétés appropriées pour tenter de cerner le problème. Par exemple, le travail est-il en état d'erreur ou l'imprimante qui dessert la file d'atteinte a-t-elle été éteinte avant l'impression du travail ?
Vous trouverez ci-dessous une série d'exemples de code. Pour obtenir l'exemple complet, consultez Diagnostic de travail d'impression problématique, exemple.
Le premier exemple de code illustre le parcours des files d'attente à l'impression. (Étape 1c ci-dessus.) La variable myPrintQueues est l'objet PrintQueueCollection pour le serveur d'impression actif.
L'exemple de code commence par actualiser l'objet de file d'attente à l'impression en utilisant PrintQueue.Refresh. Cela garantit que les propriétés de l'objet représentent exactement l'état de l'imprimante physique correspondante. L'application obtient ensuite l'ensemble des travaux d'impression actuellement placés dans la file d'attente à l'impression en utilisant GetPrintJobInfoCollection.
Ensuite, l'application parcourt la collection PrintSystemJobInfo et compare chaque propriété Submitter à l'alias de l'utilisateur plaignant. Si ces éléments concordent, l'application ajoute les informations d'identification du travail à la chaîne qui sera présentée. (Les variables userName et jobList sont initialisées précédemment dans l'application.)
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;
}
}
}
L'exemple de code suivant reprend l'application à l'étape 2. (Voir ci-dessus.) Le travail problématique a été repéré et l'application demande les informations d'identification. À partir de ces informations, elle crée les objets PrintServer, PrintQueue et PrintSystemJobInfo.
À ce stade, l'application contient une structure de branche qui correspond aux deux façons de vérifier l'état d'un travail d'impression :
Vous pouvez lire les indicateurs de la propriété JobStatus qui est du type PrintJobStatus.
Vous pouvez lire chaque propriété appropriée telle que IsBlocked et IsInError.
Cet exemple illustrant les deux méthodes, l'utilisateur a été auparavant invité à choisir celle à utiliser et a répondu par l'affirmative s'il souhaitait opter pour les indicateurs de la propriété JobStatus. Voir ci-dessous pour des informations détaillées sur les deux méthodes. Enfin, l'application utilise une méthode appelée ReportQueueAndJobAvailability pour préciser si le travail peut être imprimé à ce moment de la journée. Cette méthode est décrite dans Comment : déterminer si un travail d'impression peut être imprimé à cette heure de la journée.
// 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);
Pour vérifier l'état du travail d'impression au moyen des indicateurs de la propriété JobStatus, vous contrôlez si chaque indicateur correspondant est défini. La manière standard de vérifier si l'un des bits d'un ensemble de bits indicateurs est défini, consiste à effectuer un ET logique en spécifiant l'ensemble d'indicateurs comme premier opérande et l'indicateur lui-même comme deuxième opérande. Comme l'indicateur lui-même n'a qu'un seul bit défini, le résultat du ET logique est que, au plus, ce même bit soit défini. Pour savoir si c'est le cas ou non, il suffit de comparer le résultat du ET logique avec l'indicateur lui-même. Pour plus d'informations, consultez PrintJobStatus, l'&, opérateur (Référence C#) et FlagsAttribute.
Pour chaque attribut dont le bit est défini, le code affiche cette information sur l'écran de la console et propose parfois une réponse possible. (La méthode HandlePausedJob qui est appelée si le travail ou la file d'attente sont suspendus est abordée ci-dessous.)
// 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.");
}
};
Pour vérifier l'état du travail d'impression à l'aide de propriétés individuelles, il vous suffit de lire chaque propriété et, si elle a la valeur true, d'afficher cette information sur l'écran de la console en proposant éventuellement une réponse possible. (La méthode HandlePausedJob qui est appelée si le travail ou la file d'attente sont suspendus est abordée ci-dessous.)
// 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.");
}
};
La méthode HandlePausedJob permet à l'utilisateur d'une application de reprendre à distance des travaux suspendus. Comme la file d'attente à l'impression a peut-être été suspendue pour de bonnes raisons, la méthode demande à l'utilisateur de confirmer sa décision de reprise. En cas de réponse affirmative, la méthode PrintQueue.Resume est appelée.
L'utilisateur est ensuite invité à confirmer si le travail lui-même doit reprendre, juste au cas où il ait été suspendu indépendamment de la file d'attente à l'impression. Comparez PrintQueue.IsPaused et PrintSystemJobInfo.IsPaused. Si l'utilisateur répond par l'affirmative, PrintSystemJobInfo.Resume est appelée, sinon c'est 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();
}
}
};
Voir aussi
Concepts
Documents dans Windows Presentation Foundation
Vue d'ensemble de l'impression