views:

460

answers:

1

I've written a DLL that monitors our netowork printer. The printer is connected to the server via USB cable. When I print something directly from the server, it displays information about pages sent/printed properly, but when I print something from one of the client computers it only displays its IP, sent/printed is always 0.

I suppose this is a shortcoming of the winspooler API, my question is, is there any way around it?

Here's the code, for what its worth:

typedef struct PRINTERMONITOR_API tagPRINTJOBINFO
{
    // name of the machine that printed
    char *machineName;
    // name of the document
    char *document;
    // total pages actually *printed*
    DWORD totalPages;
    // number of pages sent to the printer
    DWORD printed;
    // private: state of the printJob
    BOOL  done;
} printJobInfo_t;

//-------------------------------------------------------
// Function called when the print job event finishes
//-------------------------------------------------------
typedef void (*Callback)(printJobInfo_t *);

//-------------------------------------------------------
// Printer Monitor class
//-------------------------------------------------------
class PrinterMonitor
{
private:

    //-------------------------------------------------------
    // Handle to the printer.
    //-------------------------------------------------------
    HANDLE     m_hPrinter;

    //-------------------------------------------------------
    // Handle to the notify object being set when print event
    // occurs.
    //-------------------------------------------------------
    HANDLE     m_hNotify;

    //-------------------------------------------------------
    // Handle of our worker thread.
    //-------------------------------------------------------
    HANDLE     m_hThread;

    //-------------------------------------------------------
    // Holds PRINTER_CHANGE_XXX_XXX information about current
    // event.
    //-------------------------------------------------------
    DWORD     m_dwChanged;

    //-------------------------------------------------------
    // ID of the worker thread.
    //-------------------------------------------------------
    DWORD     m_dwThreadId;

    //-------------------------------------------------------
    // Name of the printer.
    //-------------------------------------------------------
    char     *m_pszPrinterName;

    //-------------------------------------------------------
    // Printer event snooping options.
    //-------------------------------------------------------
    PRINTER_NOTIFY_OPTIONS  *m_pOptions;

    //-------------------------------------------------------
    // Output of the event function.
    //-------------------------------------------------------
    PRINTER_NOTIFY_INFO  *m_pOutPut; 

    //-------------------------------------------------------
    // Shall we go away?
    //-------------------------------------------------------
    BOOL     m_fExit;

public:
    //-------------------------------------------------------
    // Callback function called when the event fires.
    //-------------------------------------------------------
    Callback    m_fpCallback;

    //-------------------------------------------------------
    // Constructor
    // - name of the printer
    // - callback function
    //-------------------------------------------------------
    PrinterMonitor(char *printerName, Callback callback)
    {
     memset(this, 0, sizeof(PrinterMonitor)); // zero me
     m_fpCallback  = callback;
     m_pszPrinterName = new char[strlen(printerName)];
     strcpy(m_pszPrinterName, printerName);
     m_pOptions   = new PRINTER_NOTIFY_OPTIONS;
     m_hThread   = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PrinterMonitor::StartMonitoring, this, CREATE_SUSPENDED, &m_dwThreadId);
    }

    //-------------------------------------------------------
    // Stop the worker thread and delete some shit
    //-------------------------------------------------------
    ~PrinterMonitor()
    {
     StopMonitor();
     delete(m_pOptions);
     delete[](m_pszPrinterName);
    }

    //-------------------------------------------------------
    // Run the monitor in a seperate thread
    //-------------------------------------------------------
    void RunMonitor()
    {
     ResumeThread(m_hThread);
    }

    //-------------------------------------------------------
    // Stop the monitor, noes.
    //-------------------------------------------------------
    void StopMonitor()
    {
     m_fExit = true;
    }

    //-------------------------------------------------------
    // Checks if this object is a valid monitor
    //-------------------------------------------------------
    BOOL CheckMonitor()
    {
     if(OpenPrinter(this->m_pszPrinterName, &this->m_hPrinter, NULL))
     {
      if(this->m_hPrinter != INVALID_HANDLE_VALUE)
      {
       ClosePrinter(this->m_hPrinter);
       return true;
      }
     }

     return false;
    }

private:
    //-------------------------------------------------------
    // The worker thread proc
    //-------------------------------------------------------
    static void WINAPI StartMonitoring(void *thisPtr)
    {

     PrinterMonitor* pThis = reinterpret_cast<PrinterMonitor*>(thisPtr);

     if(OpenPrinter(pThis->m_pszPrinterName, &pThis->m_hPrinter, NULL))
     {
      WORD wJobFields[5];
      wJobFields[0]   = JOB_NOTIFY_FIELD_STATUS;
      wJobFields[1]   = JOB_NOTIFY_FIELD_MACHINE_NAME;
      wJobFields[2]   = JOB_NOTIFY_FIELD_TOTAL_PAGES;
      wJobFields[3]   = JOB_NOTIFY_FIELD_PAGES_PRINTED;
      wJobFields[4]   = JOB_NOTIFY_FIELD_DOCUMENT;

      PRINTER_NOTIFY_OPTIONS_TYPE rOptions[1];
      rOptions[0].Type = JOB_NOTIFY_TYPE;
      rOptions[0].pFields = &wJobFields[0];
      rOptions[0].Count = 5;

      pThis->m_pOptions->Count = 1;
      pThis->m_pOptions->Version  = 2;
      pThis->m_pOptions->pTypes = rOptions;
      pThis->m_pOptions->Flags = PRINTER_NOTIFY_OPTIONS_REFRESH;

      if((pThis->m_hNotify = 
       FindFirstPrinterChangeNotification(pThis->m_hPrinter, 0, 0, pThis->m_pOptions)) != INVALID_HANDLE_VALUE)
      {
       while(pThis->m_hNotify != INVALID_HANDLE_VALUE && !pThis->m_fExit)
       {
        if(WaitForSingleObject(pThis->m_hNotify, 10) == WAIT_OBJECT_0)
        {
         FindNextPrinterChangeNotification(pThis->m_hNotify, &pThis->m_dwChanged, (LPVOID)pThis->m_pOptions, (LPVOID*)&pThis->m_pOutPut);

         printJobInfo_t info = {0};
         for (unsigned int i=0; i < pThis->m_pOutPut->Count; i++)
         {
          PRINTER_NOTIFY_INFO_DATA data = pThis->m_pOutPut->aData[i];

          if(data.Type == JOB_NOTIFY_TYPE)
          {
           switch(data.Field
           {
            case JOB_NOTIFY_FIELD_PAGES_PRINTED:
             {
              info.printed = data.NotifyData.adwData[0];
             }
             break;

            case JOB_NOTIFY_FIELD_MACHINE_NAME:
             {
              LPSTR name = (LPSTR) data.NotifyData.Data.pBuf;
              unsigned int length = strlen(name); 
              info.machineName = new char[length];
              strcpy(info.machineName, name);           
             }
             break;

            case JOB_NOTIFY_FIELD_TOTAL_PAGES:
             {
              info.totalPages = data.NotifyData.adwData[0];
             }
             break;

            case JOB_NOTIFY_FIELD_DOCUMENT:
             {
              LPSTR document = (LPSTR) data.NotifyData.Data.pBuf;
              unsigned int length = strlen(document);
              info.document = new char[length];
              strcpy(info.document, document);           
             }
             break;

            case JOB_NOTIFY_FIELD_STATUS:
             {
              if(data.NotifyData.adwData[0] & JOB_STATUS_PRINTED)
              {
               info.done = true;
              }
             }
             break;
           }
          }
         }

         if(info.done)
         {
          pThis->m_fpCallback(&info);

          delete[](info.document);
          delete[](info.machineName);
         }
        }
       }

       if(pThis->m_hPrinter != INVALID_HANDLE_VALUE)
        ClosePrinter(pThis->m_hPrinter);

       if(pThis->m_hNotify != INVALID_HANDLE_VALUE)
        FindClosePrinterChangeNotification(pThis->m_hNotify);

      }
     }
    }
};
+1  A: 

Got it. The network printer sends only basic info if it was added automatically by windows.

Adding the printer manually fixed the problem.

arul