在C#中尝试清空打印队列时出现了访问被拒绝的错误

10

我正在尝试在C#中编写一个方法,以清空打印队列中的所有项目。以下是我的代码:

LocalPrintServer localPrintServer = new LocalPrintServer(PrintSystemDesiredAccess.AdministratePrinter); 
PrintQueue printQueue = localPrintServer.GetPrintQueue(printerName);

if (printQueue.NumberOfJobs > 0)
{
    printQueue.Purge();
}

当这段代码运行时,在localPrintServer构造函数上,应用程序会抛出此错误:“创建PrintServer对象时发生异常。Win32错误:拒绝访问。”

该构造函数有几个重载版本(包括不发送任何参数)。尝试使用其中任何一个,我可以通过该行,但是当我到达printQueue.Purge()调用时,我得到与上述相同的访问被拒绝信息。

请提供建议以解决此问题。我可以手动从我的计算机中删除打印作业。我不确定应用程序是否以我拥有的相同访问权限运行,也不知道如何检查。


你尝试以管理员身份运行你的进程/Visual Studio了吗? - faester
我将Visual Studio的EXE设置为始终以管理员身份运行,重新启动了Visual Studio 2010并尝试了一遍。仍然收到相同的访问被拒绝错误。 - Kris
尝试右键单击输出的 .exe 文件(YourProgramName.exe),然后选择以管理员身份运行。 - mellamokb
您的用户帐户权限不足。请转到“开始菜单”,选择“打印机和设备”,右键单击打印机,选择“打印机属性”,然后进入“安全”选项卡。如需更多信息,请在superuser.com上提出问题。 - Hans Passant
这个问题解决了吗?我遇到了同样的问题。 - Vincent Hubert
6个回答

19

这个问题是由于 GetPrintQueue 方法有点恶劣,因为它不允许您传入所需的访问级别。根据你的代码,你正在使用 AdministratePrinter 权限连接到打印服务器(这是无意义的),并使用默认用户权限连接到打印队列。因此,即使 Everyone 在打印队列上拥有管理员权限,操作也会失败。

要解决此问题,请改用 PrintQueue 构造函数,并指定正确的访问级别:

using (PrintServer ps = new PrintServer()) {
    using (PrintQueue pq = new PrintQueue(ps, printerName,
          PrintSystemDesiredAccess.AdministratePrinter)) {
        pq.Purge();
    }
}

如果您没有在管理员组的上下文环境中运行(或没有以提升的权限运行),则此操作仍可能导致权限错误,因此在生产代码中将其包含在try / catch块中是一个好主意。


这非常有帮助。我还尝试使用GetPrintQueue()方法进行更改。+1 - HiTech
非常好的洞察力,即通过PrintServer请求的权限不会被通过它检索到的PrintQueue继承! - j0tt

2
你的网站是否正在运行4.0版本?当我们将网站从3.5升级到4.0框架时,我遇到了问题。在4.0框架中,打印清除功能停止工作。最终,我创建了一个使用3.5框架的Web服务,并让4.0网站与它通信,以便清除打印机。对不起,重新激活这个线程,这是我在寻找答案时遇到的一个线程。如果有人遇到相同的情况,希望这篇文章能帮助到他们。

1

我尝试使用@mdb的解决方案,但它没有起作用(使用Framework .NET 4.6.1时出现访问被拒绝的错误)。所以我最终使用了以下解决方案:

void CleanPrinterQueue(string printerName)         
{   
   using (var ps = new PrintServer())
   {
      using (var pq = new PrintQueue(ps, printerName, PrintSystemDesiredAccess.UsePrinter))
      {
         foreach (var job in pq.GetPrintJobInfoCollection())
            job.Cancel();
      }
   }
}

0

最近,在将 .net framework 从 4.0 升级到 4.6.1 后,我遇到了相同的问题。 奇怪的是,我的 .net 应用程序正在运行 .net 3.5,但不知何故受到了此更改的影响。

无论如何,我运行应用程序的方式是通过任务计划程序,修复方法是右键单击任务,通用,勾选名为“以最高特权运行”的框。

我认为如果您在控制台上运行它,需要在打开 cmd 窗口时选择“以管理员身份运行”。


0
如果您不介意清除本地计算机上的所有队列,可以使用以下代码片段。它需要管理员权限,但不会抛出异常:
System.ServiceProcess.ServiceController controller = new   System.ServiceProcess.ServiceController("Spooler");
                controller.Stop();
                System.IO.DirectoryInfo info = new System.IO.DirectoryInfo(@"C:\Windows\System32\spool\PRINTERS");

                var files = info.GetFiles();

                foreach (var file in files)
                {
                    file.Delete();
                }
                controller.Start();

0

//以此作为一个例子,帮助你开始...

 /// <summary>
 /// Cancel the print job. This functions accepts the job number.
 /// An exception will be thrown if access denied.
 /// </summary>
 /// <param name="printJobID">int: Job number to cancel printing for.</param>
 /// <returns>bool: true if cancel successfull, else false.</returns>
 public bool CancelPrintJob(int printJobID)
 {
      // Variable declarations.
      bool isActionPerformed = false;
      string searchQuery;
      String jobName;
      char[] splitArr;
      int prntJobID;
      ManagementObjectSearcher searchPrintJobs;
      ManagementObjectCollection prntJobCollection;
      try
      {
            // Query to get all the queued printer jobs.
           searchQuery = "SELECT * FROM Win32_PrintJob";
           // Create an object using the above query.
           searchPrintJobs = new ManagementObjectSearcher(searchQuery);
          // Fire the query to get the collection of the printer jobs.
           prntJobCollection = searchPrintJobs.Get();

           // Look for the job you want to delete/cancel.
           foreach (ManagementObject prntJob in prntJobCollection)
           {
                 jobName = prntJob.Properties["Name"].Value.ToString();
                 // Job name would be of the format [Printer name], [Job ID]
                 splitArr = new char[1];
                 splitArr[0] = Convert.ToChar(",");
                 // Get the job ID.
                 prntJobID = Convert.ToInt32(jobName.Split(splitArr)[1]);
                 // If the Job Id equals the input job Id, then cancel the job.
                 if (prntJobID == printJobID)
                 {
                       // Performs a action similar to the cancel
                       // operation of windows print console
                       prntJob.Delete();
                       isActionPerformed = true;
                       break;
                  }
           }
           return isActionPerformed;
      }
      catch (Exception sysException)
      {
           // Log the exception.
           return false;
       }
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接