/// <summary> /// Initializes all fields in the print job monitor. /// </summary> private void Initialize() { // In the event that the monitor is started while the spooler is offline, // keep retrying until the spooler becomes available. SafePrinterHandle hPrinter = null; while (true) { try { void action() => hPrinter = OpenPrinter(null); Retry.WhileThrowing <Win32Exception>(action, 5, TimeSpan.FromSeconds(30)); break; } catch (Win32Exception ex) { switch (ex.NativeErrorCode) { // Ignore these errors. case RPC_S_SERVER_UNAVAILABLE: case RPC_S_CALL_FAILED: case ERROR_INVALID_PRINTER_NAME: Thread.Sleep(TimeSpan.FromSeconds(1)); break; default: throw; } } } _options = CreatePrinterNotifyOptions(); SafePrinterChangeNotificationHandle hChange = FindFirstPrinterChangeNotification(hPrinter, PrinterChanges.Job, _options); _hPrinters.Add(hPrinter); _hChanges.Add(hChange); }
internal static extern bool FindNextPrinterChangeNotification(SafePrinterChangeNotificationHandle hChange, [Out] out uint pdwChange, ref PrinterNotifyOptions pPrinterNotifyOptions, [Out] out SafePrinterNotifyInfoHandle ppPrinterNotifyInfo);
internal static extern SafePrinterChangeNotificationHandle FindFirstPrinterChangeNotification(SafePrinterHandle hPrinter, uint printerChanges, uint fdwOptions, ref PrinterNotifyOptions pPrinterNotifyOptions);
/// <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); }
/// <summary> /// Creates a change notification object and returns a handle to the object. /// </summary> /// <param name="hPrinter">A handle to the printer or print server to monitor.</param> /// <param name="filter">The <see cref="PrinterChanges" /> that should trigger new events.</param> /// <param name="options">The <see cref="PrinterNotifyOptions" /> that dictate which notifications should be sent.</param> /// <returns>A handle to a change notification object associated with the specified printer or print server.</returns> /// <exception cref="Win32Exception">The underlying operation failed.</exception> private static SafePrinterChangeNotificationHandle FindFirstPrinterChangeNotification(SafePrinterHandle hPrinter, PrinterChanges filter, PrinterNotifyOptions options) { uint fdwOptions = 0; // Must always be zero for 2D printers. SafePrinterChangeNotificationHandle result = NativeMethods.FindFirstPrinterChangeNotification(hPrinter, (uint)filter, fdwOptions, ref options); if (Marshal.GetLastWin32Error() > 0) { throw new Win32Exception(); } return(result); }