最近写一个打印机监控程序,用来监控网络中共享使用的打印机的打印记录,打印机可能是以安装了Windows2000,Windows XP或更高版本的Windows操作系统的PC机共享出来的,也可能是以终端方式共享的网络打印机或绘图仪。 
    在程序运行过程中,遇到一些问题,我使用FindNextPrinterChangeNotificationByLong()函数取得打印事件的数据包,在JOB_NOTIFY_FIELD_TOTAL_PAGES 消息中取得文档的页数,这个页数数据是正确的,但在多份打印的情况下,它的页数仍然是打印一份文档时的页数,我在DEVMODE中取得dmCopies数据,可是这个份数也一直是1,请问,如何在多份打印时取得正确的打印总页数? 
    另外,对于绘图仪,有时监测到的打印纸张尺寸不正确,甚至是长度和宽度都为0,绘图仪是否与普通打印机的信息不一样,该如何解决?
    忘各位指点一、二,谢谢

解决方案 »

  1.   

    請參看這段代碼﹕BOOL GetJobs(HANDLE hPrinter,        /* Handle to the printer. */                 JOB_INFO_2 **ppJobInfo, /* Pointer to be filled.  */ 
                    int *pcJobs,            /* Count of jobs filled.  */ 
                    DWORD *pStatus)         /* Print Queue status.    */    {   DWORD               cByteNeeded,
                            nReturned,
                            cByteUsed;
        JOB_INFO_2          *pJobStorage = NULL;
        PRINTER_INFO_2       *pPrinterInfo = NULL;   /* Get the buffer size needed. */ 
           if (!GetPrinter(hPrinter, 2, NULL, 0, &cByteNeeded))
           {
               if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
                   return FALSE;
           }       pPrinterInfo = (PRINTER_INFO_2 *)malloc(cByteNeeded);
           if (!(pPrinterInfo))
               /* Failure to allocate memory. */ 
               return FALSE;       /* Get the printer information. */ 
           if (!GetPrinter(hPrinter,
                   2,
                   (LPSTR)pPrinterInfo,
                   cByteNeeded,
                   &cByteUsed))
           {
               /* Failure to access the printer. */ 
               free(pPrinterInfo);
               pPrinterInfo = NULL;
               return FALSE;
           }       /* Get job storage space. */ 
           if (!EnumJobs(hPrinter,
                   0,
                   pPrinterInfo->cJobs,
                   2,
                   NULL,
                   0,
                   (LPDWORD)&cByteNeeded,
                   (LPDWORD)&nReturned))
           {
               if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
               {
                   free(pPrinterInfo);
                   pPrinterInfo = NULL;
                   return FALSE;
               }
           }       pJobStorage = (JOB_INFO_2 *)malloc(cByteNeeded);
           if (!pJobStorage)
           {
               /* Failure to allocate Job storage space. */ 
               free(pPrinterInfo);
               pPrinterInfo = NULL;
               return FALSE;
           }       ZeroMemory(pJobStorage, cByteNeeded);       /* Get the list of jobs. */ 
           if (!EnumJobs(hPrinter,
                   0,
                   pPrinterInfo->cJobs,
                   2,
                   (LPBYTE)pJobStorage,
                   cByteNeeded,
                   (LPDWORD)&cByteUsed,
                   (LPDWORD)&nReturned))
           {
               free(pPrinterInfo);
               free(pJobStorage);
               pJobStorage = NULL;
               pPrinterInfo = NULL;
               return FALSE;
           }       /*
            *  Return the information.
            */ 
           *pcJobs = nReturned;
           *pStatus = pPrinterInfo->Status;
           *ppJobInfo = pJobStorage;
           free(pPrinterInfo);       return TRUE;   }   BOOL IsPrinterError(HANDLE hPrinter)
       {       JOB_INFO_2  *pJobs;
           int         cJobs,
                       i;
           DWORD       dwPrinterStatus;       /*
            *  Get the state information for the Printer Queue and
            *  the jobs in the Printer Queue.
            */ 
           if (!GetJobs(hPrinter, &pJobs, &cJobs, &dwPrinterStatus))
               return FALSE;       /*
            *  If the Printer reports an error, believe it.
            */ 
           if (dwPrinterStatus &
               (PRINTER_STATUS_ERROR |
               PRINTER_STATUS_PAPER_JAM |
               PRINTER_STATUS_PAPER_OUT |
               PRINTER_STATUS_PAPER_PROBLEM |
               PRINTER_STATUS_OUTPUT_BIN_FULL |
               PRINTER_STATUS_NOT_AVAILABLE |
               PRINTER_STATUS_NO_TONER |
               PRINTER_STATUS_OUT_OF_MEMORY |
               PRINTER_STATUS_OFFLINE |
               PRINTER_STATUS_DOOR_OPEN))
           {
               return TRUE;
           }       /*
            *  Find the Job in the Queue that is printing.
            */ 
           for (i=0; i < cJobs; i++)
           {
               if (pJobs[i].Status & JOB_STATUS_PRINTING)
               {
                   /*
                    *  If the job is in an error state,
                    *  report an error for the printer.
                    *  Code could be inserted here to
                    *  attempt an interpretation of the
                    *  pStatus member as well.
                    */ 
                   if (pJobs[i].Status &amp;
                       (JOB_STATUS_ERROR |
                       JOB_STATUS_OFFLINE |
                       JOB_STATUS_PAPEROUT |
                       JOB_STATUS_BLOCKED_DEVQ))
                   {
                       return TRUE;
                   }
               }
           }       /*
            *  No error condition.
            */ 
           return FALSE;   }
      

  2.   


      确定打印队列的状态问题
      有时,程序员需要显示存在于系统中的各种打印机中打印作业的状态,但似乎不能找到一种方法来查询当前哪些打印机是可用的以及在这些打印机上正在运行哪些作业。
      那么,如何利用 Windows API 函数来查询可用的打印机及其所处的工作状态呢?方法
      在 Windows NT 版本以及 Windows 9x 中,增加了一批全新的、专门用来处理打印机及打印机队列状态的 API 函数。在本节中,将讨论 API 函数 EnumPrinters 与 EnumJobs,这两个函数可提供给程序员确定打印作业状态所需的信息。
      在 Windows 9x 中添加的函数 EnumPrinters,用来列出所有正在有效运行的打印机的名称及其标识符,此函数可用于列出用户能够访问的无论是在本地工作的还是在网络上运行的所有打印机,在本节的例子中,我们只考虑本地打印机的情况。
      新的 Windows 9x API 中增加的另一个函数 EnumJobs,用来列出所指定打印机上正在打印的作业,为了能够使用这个函数,必须有打印机的句柄,这就需要使用另外一个 API 函数 OpenPrinter。
      需要注意的是,这几个 API 函数只能在 Windows 9x 以及 Windows NT 中使用。步骤
      按照下列方法生成一个例子程序。选择菜单 Dialog,并从此菜单中选择菜单项 Printer Jobs,此时显示一个所有可用打印机的对话框,在第一个列表框中选择其中一个打印机后,即在第二个列表框中显示该打印机的作业列表。  实现例子程序的步骤如下:
      1.在 Visual C++ 中利用 AppWizard 创建新的工程 Ld104。
      2.进入 AppStudio,创建新的对话框。把按钮 OK 与 Cancel 移动到对话框的底部。在对话框中添加两个列表框。
      3.进入 ClassWizard,为刚创建的对话模板生成新的对话框类,把此新类命名为 CPrintQDlg。
      4.在 ClassWizard 中,从下拉列表中选择类 CPrintQDlg,从对象列表中选择对象 CPrintQDlg,从消息列表中选择消息 WM_INITDIALOG,点击按钮 Add Function 添加新的函数 OnInitDialog。把下面的代码输入到类 CPrintQDlg 的方法 OnInitDialog 中。
    BOOL CPrintQDlg::OnInitDialog() 
    {
        CDialog::OnInitDialog();    int size=4096;
        unsigned long sizeNeeded=0;
        unsigned long numPrinters;
        PPRINTER_INFO_1 pPrinters;
        pPrinters=(PPRINTER_INFO_1)LocalAlloc((LMEM_FIXED/LMEM_ZEROINIT),size);
        int ret=EnumPrinters(PRINTER_ENUM_LOCAL,NULL,1,
            (LPBYTE)pPrinters,size,&sizeNeeded,&numPrinters);
        CListBox * list=(CListBox *)GetDlgItem(IDC_LIST1);
        for(int i=0;i<(int)numPrinters;++i)
        list->AddString(pPrinters[i].pName);
        LocalFree(pPrinters);    return TRUE;  // return TRUE unless you set the focus to a control
    }  5.在 ClassWizard 中,从下拉列表中选择类 CPrintQDlg,从对象列表中选择对象 IDC_LIST1,从消息列表中选择消息 LBN_SELCHANGE,点击按钮 Add Function 添加新的函数 OnSelectPrinter。把下面的代码输入到类 CPrintQDlg 的方法 OnSelectPrinter 中。void CPrintQDlg::OnSelectPrinter()
    {
        HANDLE handle;
        JOB_INFO_1 jobs[10];    CListBox * list1=(CListBox *)GetDlgItem(IDC_LIST1);
        char buf[256];
        list1->GetText(list1->GetCurSel(),buf);    if(!OpenPrinter(buf,&handle,NULL))
        {
        MessageBox("Could not open printer","Error",
            MB_OK/MB_ICONINFORMATION);
        return;
        }    DWORD size=sizeof(jobs);
        DWORD numBytes=0;
        DWORD numEntries=0;
        int ret=EnumJobs(handle,0,10,1,(LPBYTE)jobs,
            size,&numBytes,&numEntries);    CListBox * list2=(CListBox *)GetDlgItem(IDC_LIST2);
        for(int i=0;i<(int)numEntries;++i)
        {
        char buf[80];
        sprintf(buf,"Job %ld",jobs[i].JobId);
        if(jobs[i].pStatus!=NULL)
            strcat(buf,jobs[i].pStatus);
        else
        {
            switch(jobs[i].Status)
            {
            case JOB_STATUS_PAUSED:
                strcat(buf," Paused");
                break;
            case JOB_STATUS_PRINTED:
                strcat(buf," Printed");
                break;
            case JOB_STATUS_PRINTING:
                strcat(buf," Printing");
                break;
            case JOB_STATUS_SPOOLING:
                strcat(buf," Spooling");
                break;
            }
        }
        list2->AddString(buf);
        }
    }  6.把下面一行代码添加到文件 PrintQDlg.cpp 的顶端。
      #include "winspool.h"
      7.进入 AppStudio 并在菜单 IDR_MAINFRAME 中添加新菜单 Dialog。在此菜单中添加标题为 Printer Jobs 的菜单项,标识符命名为 ID_PRINTER_JOBS。
      8.进入 ClassWizard,从下拉组合框中选择对象 CMainFrame,从对象列表中选择对象 ID_PRINTER_JOBS,从消息列表中选择消息 COMMAND,点击按钮 Add Function 添加新的函数 OnPrinterJobs,把下面的代码输入到此方法中。void CMainFrame::OnPrinterJobs()
    {
        CPrintQDlg dlg;    dlg.DoModal();
    }  9.把下面一行代码添加到文件 MainFrame.cpp 的顶端。
      #include "PrintQDlg.h"
      10.编译并运行此例子程序。
     
     
      

  3.   

    更完整的例子可以参考微软的Knowledge Base的文章:“Q158828 OWTO: How To Call Win32 Spooler Enumeration APIs Properly”和微软的Knowledge Base的文章:“Q228769 OWTO: Retrieve Print Job Information”。 http://support.microsoft.com/support/kb/articles/Q158/8/28.asp
    http://support.microsoft.com/support/kb/articles/Q228/7/69.asp