Delen via


PrintMonitor – A C# print spooler monitor

Recently, a colleague of mine had a requirement of getting information on the number of pages being printed by users. Monitoring a printer is done by calling FindFirstPrinterChangeNotification, waiting for the returned WaitHandle to be signaled and then calling

FindNextPrinterChangeNotification to see why the handle got signaled and act accordingly. Sadly, there don’t seem to be any samples of this in C# out there, so I created one in my spare time. The attached application monitors all printer notification fields possible. It still has some issues like not disposing things correctly, but that’s left as an exercise to the reader, as I don’t have more time to spent.

Upon starting the application, the Load event of the main form hooks up the event and handles (it currently takes the first printer it can find):

 printerWaitHandle = new ManualResetEvent(false);

PRINTER_INFO_2[] printers = enumPrinters(PrinterEnumFlags.PRINTER_ENUM_NAME);
string printerName = printers[0].pPrinterName;
OpenPrinter(printerName, out printerHandle, 0);

printerChangeHandle = FindFirstPrinterChangeNotification(printerHandle, (int)PRINTER_CHANGES.PRINTER_CHANGE_DELETE_JOB, 0, notifyOptions);
printerWaitHandle.SafeWaitHandle = new SafeWaitHandle(printerChangeHandle, true);

printerChangeNotificationHandle = ThreadPool.RegisterWaitForSingleObject(
 printerWaitHandle, 
 new WaitOrTimerCallback(PrinterNotifyWaitCallback), printerWaitHandle, -1, true);

The callback method (PrinterNotifyWaitCallback) interprets the change:

 public void PrinterNotifyWaitCallback(object state, bool timedOut)
{
    // BUG: if we Thread.Sleep here for the amount of time it takes to spool the print job, we only get the right notification for number of pages.

    int changeReason = 0;
    IntPtr pNotifyInfo = IntPtr.Zero;

    bool nxt = FindNextPrinterChangeNotification(printerChangeHandle, out changeReason, null, out pNotifyInfo);
 ...
 }

This works quite well. There is a bug in there somewhere which causes the following behavior: the total number of pages is first set to 0 when spooling the print job and only on the *subsequent* print job being printed is the correct number of pages signaled. If you put a Thread.Sleep (don’t ;-)) in the callback for the amount of time it takes the spooler to spool the print job, the correct number of printed pages is displayed directly (without the ‘0’ entry and within the actual print job it belongs to). If anyone can find the bug, please let me know!

Sourcecode attached, as always, no warranties of any kind, express, implied, etc.

Sourcecode