/// <summary> /// Checks printer(s) for notifications. /// </summary> private void ListenForChanges() { // Check for cancellation. _cancelSource.Token.ThrowIfCancellationRequested(); SafePrinterChangeNotificationHandle hChange = null; try { hChange = WaitForMultipleObjects(_hChanges); } catch (TimeoutException) { // No object changed within the timeout period. // This could mean that there was really no change, // or because the spooler is no longer sending updates to us. // Since we can't tell the difference, clean up existing structures and sign up for events again. // If the cancel token has been set, it might be a bit before we get back to checking it again, // so check it here before resetting. If it has been set, bail out so the monitor can stop. _cancelSource.Token.ThrowIfCancellationRequested(); LogDebug("No job changes detected. Resetting listener."); Reset(); Initialize(); return; } catch (ArgumentOutOfRangeException ex) { // Unsuccessful in getting the change notification handle. Bail out. LogDebug(ex); return; } // WaitForMultipleObjects could have taken a while, so check for cancellation again. _cancelSource.Token.ThrowIfCancellationRequested(); // Get the notify information that changed. PrinterNotifyInfoReader infoReader = FindNextPrinterChangeNotification(hChange); if (infoReader == null) { LogDebug("Lost job data. Trying a refresh."); infoReader = FindNextPrinterChangeNotification(hChange, _options); } using (infoReader) { foreach (PrinterNotifyInfoData infoData in infoReader.ReadInfoData()) { if (infoData.NotifyType == NotifyType.Job) { JobNotificationReceived?.Invoke(this, new JobNotificationEventArgs(infoData)); } } } }
/// <summary> /// Retrieves information about the most recent notification for a change notification object associated with a printer or print server. /// Call this function when a wait operation on the change notification object is satisfed. /// </summary> /// <param name="hChange">A handle to a change notification object associated with a printer or print server.</param> /// <param name="options">The <see cref="PrinterNotifyOptions" />.</param> /// <returns>A <see cref="PrinterNotifyInfoReader" /> containing the notification details.</returns> /// <exception cref="Win32Exception">The underlying operation failed.</exception> /// <exception cref="System.Exception"> /// No notification available at this time. /// or /// Info has been discarded. /// </exception> private static PrinterNotifyInfoReader FindNextPrinterChangeNotification(SafePrinterChangeNotificationHandle hChange, PrinterNotifyOptions options = new PrinterNotifyOptions()) { if (!NativeMethods.FindNextPrinterChangeNotification(hChange, out _, ref options, out SafePrinterNotifyInfoHandle infoHandle)) { throw new Win32Exception(); } if (infoHandle == null || infoHandle.IsInvalid) { // Apparently there was no notification after all. Just return an empty reader. return(new PrinterNotifyInfoReader()); } PrinterNotifyInfoReader result = new PrinterNotifyInfoReader(infoHandle); // Check to see if data has been discarded. if (result.InfoDiscarded) { result.Dispose(); return(null); } return(result); }