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);
}
}
}
};