如何:诊断有问题的打印作业

更新:2007 年 11 月

网络管理员经常听到用户抱怨打印作业不打印或打印速度慢。Microsoft .NET Framework的 API 中公开了一组丰富的打印作业属性,通过这些属性可以快速地对打印作业进行远程诊断。

示例

下面是创建这种实用工具的主要步骤。

  1. 识别用户正在抱怨的打印作业。用户通常不能准确地完成此步骤。他们可能不知道打印服务器或打印机的名称。用户可能不是使用设置打印机的 Location 属性时所使用的术语来描述打印机的位置。因此,最好是生成用户当前提交作业的列表。如果有多个作业,则可以通过用户与打印系统管理员之间的通信来查明有问题的作业。具体步骤如下。

    1. 获取所有打印服务器的列表。

    2. 遍历各服务器以查询其打印队列。

    3. 在服务器遍历的每个处理过程中,遍历服务器的所有队列以查询其作业。

    4. 在队列遍历的每个阶段,遍历其作业并收集与提出问题的用户所提交的作业有关的标识信息。

  2. 识别出有问题的打印作业之后,检查相关的属性以查明可能的问题所在。例如,问题是由于打印作业处于错误状态引起的?还是由于处理队列的打印机在执行打印作业之前进入脱机状态而引起的?

下面的代码由一系列代码示例组成。有关完整示例,请参见诊断有问题的打印作业示例

第一个代码示例包含打印队列的遍历 (上述步骤 1c)。变量 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 中的应用程序(如上所示)。已识别出有问题的作业,应用程序提示输入标识此作业的信息。根据此信息创建 PrintServerPrintQueuePrintSystemJobInfo 对象。

此时,应用程序包含一个与检查打印作业的状态的两种方法相对应的分支结构:

此示例对这两种方法均进行了演示,因此用户在这之前会被提示选择要使用哪种方法,如果希望使用 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 属性的标志来检查打印作业状态,可以检查每个相关标志来查看它是否已设置。如需检查是否在一组位标志中设置了某个位,标准方法是将这组标志作为一个操作数,将需要检查的标志本身作为另一个操作数,对它们执行逻辑“与”运算。由于该标志本身只设置了一个位,因此逻辑“与”运算的结果是至多设置了同一个位。若要确定是否设置了该位,只需将逻辑“与”运算的结果与该标志本身进行比较。有关更多信息,请参见 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.IsPausedPrintSystemJobInfo.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 中的文档

打印概述

参考

PrintJobStatus

PrintSystemJobInfo

& 运算符(C# 参考)

FlagsAttribute

PrintQueue

其他资源

打印示例