Access Denied trying to purge printqueue in C#

11.4k views Asked by At

I'm trying to make a method in C# that empties all items in a print queue. Below is my code:

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

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

When this code runs, on the localPrintServer constructor, the app throws this error: "An exception occurred while creating the PrintServer object. Win32 error: Access is denied."

That constructor has a few overloads (including sending no parameters). Trying any of those, I get past that line, but when I get to the printQueue.Purge() call, I get the same access denied message as listed above.

Looking for suggestions of how / what I can do to get around this. I can manually delete the print jobs from my computer. I'm not sure if the app runs with the same access I have nor how to check that.

6

There are 6 answers

0
Griff2k On

Are you running your website as 4.0? I ran into issues when we upgraded our website from 3.5 to 4.0 Framework. The Print Purging functionality stopped working in the 4.0 Framework. Ultimately I ended up creating a web service that used the 3.5 framework and had the 4.0 website communicate the printer it wanted to purge to the 3.5 web service.

(Sorry to revive this thread, this was one of the threads I stumbled onto when I was looking for an answer. Figured I'd post this if it helps someone that runs into the same situation)

0
neolei On

Recently, I encountered the same problem after I upgrade the .net framework from 4.0 to 4.6.1. Oddly enough, my .net application was running on .net 3.5, but somehow it was affected by this change.

Anyway, the way I ran my application was via task scheduler, and the fix is to right click on the task, general, check the box named "Run with highest privileges".

I think if you run it on console, you need to "Run as administrator" when open the cmd window.

0
MethodMan On

//use this as an example to get you started...

 /// <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;
       }
 }
0
labilbe On

I tried using @mdb solution but it didn't work (Access denied using Framework .NET 4.6.1). So I ended up using the following solution:

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();
      }
   }
}
2
mdb On

This problem is caused by the GetPrintQueue method being slightly evil, since it does not allow you to pass in the desired access level. With your code as it is, you are connecting to the print server with AdministratePrinter rights (which is meaningless), and connecting to the print queue with default user rights. Thus, the operation will fail, even if Everyone has admin rights on the print queue.

To fix this, use the constructor for PrintQueue instead to specify the correct access level:

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

This may still cause permission errors if you're not running in the context of a member of the Administrators group (or not running with elevated permissions), so surrounding this with a try/catch block is a good idea for production code.

0
Vincent Hubert On

If you do not mind clearing all queues on the local machine, you can use the following snippet. It requires admin privileges, but will not throw exceptions:

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();