“ FindFirstPrinterChangeNotification”,“ FindNextPrinterChangeNotification”打印Aps的C#问题

时间:2019-09-02 17:31:06

标签: printing

我正在尝试使用C#编写的Windows Service在Windows 10 x64下监视打印队列中的作业。

我的代码中使用“ FindFirstPrinterChangeNotification”和“ FindNextPrinterChangeNotification”打印API的部分与此项目相同。(https://www.codeproject.com/Articles/51085/Monitor-jobs-in-a-printer-queue-NET

唯一的区别是我仅监视“ PRINTER_CHANGES.PRINTER_CHANGE_ADD_JOB”事件。

部分代码:

public class PrintQueueMonitor  
{
    #region DLL Import Functions
    [DllImport("winspool.drv", 
      EntryPoint = "OpenPrinterA",SetLastError = true, 
      CharSet = CharSet.Ansi,ExactSpelling = true, 
      CallingConvention = CallingConvention.StdCall)]
    public static extern bool OpenPrinter(String pPrinterName,
    out IntPtr phPrinter,
    Int32 pDefault);
    [DllImport("winspool.drv", 
        EntryPoint = "ClosePrinter",
        SetLastError = true,
        ExactSpelling = true,
        CallingConvention = CallingConvention.StdCall)]
    public static extern bool ClosePrinter
    (Int32 hPrinter);
    [DllImport("winspool.drv",
    EntryPoint = "FindFirstPrinterChangeNotification",
    SetLastError = true, CharSet = CharSet.Ansi,
    ExactSpelling = true,
    CallingConvention = CallingConvention.StdCall)]
    public static extern IntPtr FindFirstPrinterChangeNotification
                        ([InAttribute()] IntPtr hPrinter,
                        [InAttribute()] Int32 fwFlags,
                        [InAttribute()] Int32 fwOptions,
                        [InAttribute(), 
                         MarshalAs(UnmanagedType.LPStruct)] 
                         PRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions);
    [DllImport("winspool.drv", EntryPoint = 
               "FindNextPrinterChangeNotification",
    SetLastError = true, CharSet = CharSet.Ansi,
    ExactSpelling = false,
    CallingConvention = CallingConvention.StdCall)]
    public static extern bool FindNextPrinterChangeNotification
                        ([InAttribute()] IntPtr hChangeObject,
                         [OutAttribute()] out Int32 pdwChange,
                         [InAttribute(), 
                          MarshalAs(UnmanagedType.LPStruct)] 
                          PRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions,
                        [OutAttribute()] out IntPtr lppPrinterNotifyInfo
                             );
        #endregion
        #region Constants
        const int PRINTER_NOTIFY_OPTIONS_REFRESH = 1;
        #endregion
        #region Events
        public event PrintJobStatusChanged OnJobStatusChange;
        #endregion
        #region private variables
        private IntPtr _printerHandle = IntPtr.Zero;
        private string _spoolerName = "";
        private ManualResetEvent _mrEvent = new ManualResetEvent(false);
        private RegisteredWaitHandle _waitHandle = null;
        private IntPtr _changeHandle = IntPtr.Zero;
        private PRINTER_NOTIFY_OPTIONS _notifyOptions = 
                new PRINTER_NOTIFY_OPTIONS();
        private Dictionary<int, string> objJobDict = 
                new Dictionary<int, string>();
        private PrintQueue _spooler = null;
        #endregion
        #region constructor
        public PrintQueueMonitor(string strSpoolName) 
        {
            // Let us open the printer and get the printer handle.
            _spoolerName = strSpoolName;
            //Start Monitoring
            Start();
        }
        #endregion
        #region destructor
        ~PrintQueueMonitor() 
        {
            Stop();
        }
        #endregion
        #region StartMonitoring
        public void Start()
        {
            OpenPrinter(_spoolerName, out _printerHandle, 0);
            if (_printerHandle != IntPtr.Zero)
            {
                //We got a valid Printer handle. 
                //Let us register for change notification....
                _changeHandle = FindFirstPrinterChangeNotification(
                  _printerHandle, (int)PRINTER_CHANGES.PRINTER_CHANGE_JOB, 0, 
                  _notifyOptions);
                // We have successfully registered for change
                // notification. Let us capture the handle...
                _mrEvent.Handle = _changeHandle;
                //Now, let us wait for change notification from the printer queue....
                _waitHandle = ThreadPool.RegisterWaitForSingleObject(_mrEvent, 
                  new WaitOrTimerCallback(PrinterNotifyWaitCallback), 
                  _mrEvent, -1, true);
            }
            _spooler = new PrintQueue(new PrintServer(), _spoolerName);
            foreach (PrintSystemJobInfo psi in _spooler.GetPrintJobInfoCollection())
            {
                objJobDict[psi.JobIdentifier] = psi.Name;
            }
        }
        #endregion
        #region StopMonitoring
        public void Stop()
        {
            if (_printerHandle != IntPtr.Zero)
            {
                ClosePrinter((int)_printerHandle);
                _printerHandle = IntPtr.Zero;
            }
        }
        #endregion
        #region Callback Function
        public void PrinterNotifyWaitCallback(Object state,bool timedOut)
        {
            if (_printerHandle == IntPtr.Zero) return;
            #region read notification details
            _notifyOptions.Count = 1;
            int pdwChange = 0;
            IntPtr pNotifyInfo = IntPtr.Zero;
            bool bResult = FindNextPrinterChangeNotification(_changeHandle, 
                           out pdwChange, _notifyOptions, out pNotifyInfo);
            //If the Printer Change Notification Call did not give data, exit code
            if ((bResult == false) || (((int)pNotifyInfo) == 0)) return;
            //If the Change Notification was not relgated to job, exit code
            bool bJobRelatedChange = 
              ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_ADD_JOB) == 
                PRINTER_CHANGES.PRINTER_CHANGE_ADD_JOB) || 
              ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_SET_JOB) == 
                PRINTER_CHANGES.PRINTER_CHANGE_SET_JOB) ||
              ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_DELETE_JOB) == 
                PRINTER_CHANGES.PRINTER_CHANGE_DELETE_JOB) ||
              ((pdwChange & PRINTER_CHANGES.PRINTER_CHANGE_WRITE_JOB) == 
                PRINTER_CHANGES.PRINTER_CHANGE_WRITE_JOB);
            if (!bJobRelatedChange) return;
            #endregion 
            #region populate Notification Information
            //Now, let us initialize and populate the Notify Info data
            PRINTER_NOTIFY_INFO info = 
              (PRINTER_NOTIFY_INFO)Marshal.PtrToStructure(pNotifyInfo, 
               typeof(PRINTER_NOTIFY_INFO));                        
            int pData = (int)pNotifyInfo + 
                         Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO));
            PRINTER_NOTIFY_INFO_DATA[] data = 
                    new PRINTER_NOTIFY_INFO_DATA[info.Count];
            for (uint i = 0; i < info.Count; i++)
            {
                data[i] = (PRINTER_NOTIFY_INFO_DATA)Marshal.PtrToStructure(
                          (IntPtr)pData, typeof(PRINTER_NOTIFY_INFO_DATA));
                pData += Marshal.SizeOf(typeof(PRINTER_NOTIFY_INFO_DATA));
            }
            #endregion 

            #region iterate through all elements in the data array
            for (int i = 0; i < data.Count(); i++)
            {
                if ( (data[i].Field == 
                       (ushort)PRINTERJOBNOTIFICATIONTYPES.JOB_NOTIFY_FIELD_STATUS) &&
                     (data[i].Type == (ushort)PRINTERNOTIFICATIONTYPES.JOB_NOTIFY_TYPE)
                    )
                {
                   JOBSTATUS jStatus  = (JOBSTATUS)Enum.Parse(typeof(JOBSTATUS), 
                                         data[i].NotifyData.Data.cbBuf.ToString());
                    int intJobID = (int)data[i].Id;
                    string strJobName = ""; 
                    PrintSystemJobInfo pji = null;
                    try
                    {
                        _spooler = new PrintQueue(new PrintServer(), _spoolerName);
                        pji = _spooler.GetJob(intJobID);
                        if (!objJobDict.ContainsKey(intJobID))
                            objJobDict[intJobID] = pji.Name;
                        strJobName = pji.Name;
                    }
                    catch
                    {
                        pji = null;
                        objJobDict.TryGetValue(intJobID, out strJobName);
                        if (strJobName == null) strJobName = "";
                    }
                    if (OnJobStatusChange != null)
                    {
                        //Let us raise the event
                        OnJobStatusChange(this, 
                          new PrintJobChangeEventArgs(intJobID,strJobName,jStatus,pji));
                    }
                }
            }
            #endregion
            #region reset the Event and wait for the next event
            _mrEvent.Reset();
            _waitHandle = ThreadPool.RegisterWaitForSingleObject(_mrEvent, 
              new WaitOrTimerCallback(PrinterNotifyWaitCallback), _mrEvent, -1, true);
            #endregion 
        }
        #endregion
    }

问题是:当我打印某些东西时,很少收到通知,而作业ID来自

int intJobID = (int)data[i].Id;

line与我在操作系统的打印队列窗口中看到的id不同。然后,当然在下面的代码行中找不到具有该ID的作业:

try
                    {
                        _spooler = new PrintQueue(new PrintServer(), _spoolerName);
                        pji = _spooler.GetJob(intJobID);
                        if (!objJobDict.ContainsKey(intJobID))
                            objJobDict[intJobID] = pji.Name;
                        strJobName = pji.Name;
                    }

它在此行引发异常:

pji = _spooler.GetJob(intJobID);

这种情况很少见,但有可能发生。我不知道为什么会这样,怎么解决?

0 个答案:

没有答案