C# - Unable to catch end of printing

3.5k Views Asked by At

I have a printer (HP PageWide MFP P57750) connected to a private network by a ethernet cable. In a simple C# Web App, user sends a pdf file to the printer and set the number of copies to be printed. At the end of the print, the app should notify the end of the job.

I try using the EndPrint event of PrintDocument but it seems to fire in a wrong time, cause printer is still printing.

I also try using PrintQueue with PrintJobInfoCollection but it returns the "Printing" PrintJobStatus just the first time. Then, I wait for 5 seconds and recheck the status but there are no jobs in the queue while the printer is still printing!

I see in Control Panel -> Devices And Printers the progress of jobs. The print lasts just a few seconds even if there are many copies and the printer is still printing. I think the job gets deleted after client sends the file to be printed to the printer.

How can i catch the real end of printing?

PdfDocument pdf = new PdfDocument();
pdf.LoadFromFile(filePath);
pdf.PrinterName = CicloStampa.Properties.Settings.Default.NomeStampante;
pdf.PrintDocument.PrinterSettings.Copies = (short)numeroCopie;          

pdf.PrintDocument.Print();

PrintServer myPrintServer = new PrintServer();
PrintQueue pq = myPrintServer.GetPrintQueue(CicloStampa.Properties.Settings.Default.NomeStampante);
var printing = true;

while (printing)
{
    pq.Refresh();
    PrintJobInfoCollection pCollection = pq.GetPrintJobInfoCollection();

    foreach (PrintSystemJobInfo job in pCollection)
    {
       //SpotTroubleUsingJobAttributes returns KO/OK by matching PrintJobStatus. First time returns PrintJobStatus.Priting. Second time, pCollection is empty and the printer is still printing.
       var res = SpotTroubleUsingJobAttributes(job);
       System.IO.File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Logs\LogImportazione.txt", DateTime.Now.ToString() + " ---- \t\t\t RES: " + res + "\n");
       if (res.StartsWith("KO"))
       {
          //DO STUFF                       
       }
       else
       {
          //END OF PRINT
          printing = false;
        }
     }
     if (pCollection.Count() == 0)
     {                                    
        printing = false;
     }
     if (printing)
     {                                                                       
         System.Threading.Thread.Sleep(5000);
     }
}
2

There are 2 best solutions below

0
On

So I have been working with PDF24 printer, but this should work for all printers.

Thanks to @mark_h for providing the code. Using his code example I managed to find a method to know when the print queue is "done". You are going to have to pause the print queue prior to printing, so depending on the circumstances, some execution time should be allowed for.

Once I finished adding to the print queue, I will count the documents in it so I know when the printer is done printing them all. Then in the second loop we will keep subtracting the documents as they are printed. Once we reach the final document in the queue we break out of the loop and put the thread to sleep for a few seconds, giving the printer time to finish it.

We are then free to execute what ever code knowing that the print jobs has all been printed.

To catch the end of printing, the following code will work:

            PrintServer printS = new PrintServer();
            PrintQueue printQ = new PrintQueue(printS, "PDF24", PrintSystemDesiredAccess.AdministratePrinter);
            printQ.Pause();


            // Now right here place printing logic, after queue is paused

          
            PrintJobInfoCollection jobs = printQ.GetPrintJobInfoCollection();
            int printCount = 0;

            foreach (var job in jobs)
            {
                printCount++;
            }

            printQ.Resume();

            foreach (var job in jobs)
            {
                var done = false;

                while (!done)
                {
                    printQ.Refresh();
                    job.Refresh();
                    done = job.IsCompleted || job.IsDeleted || job.IsPrinted;
                }

                if (done)
                {
                    printCount--;
                }

                if (printCount < 1)
                {
                    break;
                }
            }

            // Allow some time for the last document to execute, then run what ever code you want
            Thread.Sleep(3000);

I hope this helps, otherwise drop a comment and I can provide more code examples if needed. Can't believe it has to be so difficult to figure out whether a printer is done printing or not.

4
On

You could get a collection of the jobs on the print queue then keep looping until you get the job status you are looking for.

        var myPrintServer = new LocalPrintServer();
        var pq = myPrintServer.GetPrintQueue("Printer Name");

        var jobs = pq.GetPrintJobInfoCollection();

        foreach (var job in jobs)
        {
            var done = false;
            while (!done)
            {
                pq.Refresh();
                job.Refresh();
                done = job.IsCompleted || job.IsDeleted || job.IsPrinted;
            }
        }

EDIT

The PrintQueue also has properties such as IsPrinting, IsWaiting, IsBusy etc, you could look at these properties too in your while loop but these would tell you about the printer in general, not about your jobs specifically so if someone else decides to print when you do you could get misleading information