打印机状态变更通知无法接收问题求助(附测试代码)
问题:监控打印机状态变更无通知,但作业状态监控正常
我想开发一款能在打印机打印时显示提示信息的应用。我写了一个控制台测试程序,但接收不到打印机状态变更的通知。需要说明的是,当我用JOB_NOTIFY_TYPE和JOB_NOTIFY_FIELD_STATUS监控作业状态时,能正常收到通知。
请问打印机是否不会发送状态变更通知?这是否和打印机类型或驱动有关?
测试代码
program PrinterMonitor; {$APPTYPE CONSOLE} uses Winapi.Windows, Winapi.WinSpool; const PrinterField: WORD = PRINTER_NOTIFY_FIELD_STATUS; var PrinterName: string; hStopEvent: THandle; hPrinter: THandle; hPrintNotify: THandle; pno: TPrinterNotifyOptions; pnot: TPrinterNotifyOptionsType; pni: Pointer; dwChange: DWORD; WaitHandles: array[0..1] of THandle; function CtrlHandler(fdwCtrlType: DWORD): BOOL; stdcall; begin Result := False; case fdwCtrlType of CTRL_C_EVENT, CTRL_CLOSE_EVENT: begin SetEvent(hStopEvent); Result := True; end; end; end; procedure GetPrinterNotifyData(const Info: TPrinterNotifyInfo); var I: Integer; begin for I := 0 to Info.Count - 1 do begin with Info.aData[I] do begin case wType of PRINTER_NOTIFY_TYPE: begin case Field of PRINTER_NOTIFY_FIELD_STATUS: WriteLn('Printer status: ', NotifyData.adwData[0]); end; end; end; end; end; end; begin if ParamCount < 1 then begin WriteLn('No printer name specified.'); Exit; end; PrinterName := ParamStr(1); //pass a printer name as argument OpenPrinter(PWideChar(PrinterName), hPrinter, nil); if hPrinter = 0 then begin WriteLn('Cannot open printer "', PrinterName, '". ErrorCode: ', GetLastError); Exit; end; pnot.wType := PRINTER_NOTIFY_TYPE; pnot.Count := 1; pnot.pFields := @PrinterField; pno.Version := 2; pno.Count := 1; pno.pTypes := @pnot; hStopEvent := CreateEvent(nil, True, False, nil); if hStopEvent = 0 then begin WriteLn('Cannot create stop event. ErrorCode: ', GetLastError); ClosePrinter(hPrinter); Exit; end; hPrintNotify := FindFirstPrinterChangeNotification(hPrinter, 0, 0, @pno); if hPrintNotify = 0 then begin WriteLn('Cannot start monitoring. ErrorCode: ', GetLastError); ClosePrinter(hPrinter); CloseHandle(hStopEvent); Exit; end; WaitHandles[0] := hStopEvent; WaitHandles[1] := hPrintNotify; SetConsoleCtrlHandler(@CtrlHandler, True); // handling CTRL+C & close console events for stop monitoring WriteLn('==== Monitor started ===='); while True do begin case WaitForMultipleObjects(2, @WaitHandles, False, INFINITE) of WAIT_OBJECT_0: // hStopEvent signalled begin WriteLn('==== Monitor stopped ===='); Break; end; WAIT_OBJECT_0+1: // hPrintNotify signalled begin if not FindNextPrinterChangeNotification(hPrintNotify, dwChange, nil, pni) then begin WriteLn('Cannot get notify changes data.'); Break; end; GetPrinterNotifyData(PPrinterNotifyInfo(pni)^); FreePrinterNotifyInfo(PPrinterNotifyInfo(pni)); end; else begin WriteLn('Wait failed'); Break; end; end; end; FindClosePrinterChangeNotification(hPrintNotify); ClosePrinter(hPrinter); CloseHandle(hStopEvent); end.
回答
打印机状态通知的触发逻辑
打印机状态变更通知并非针对打印作业的提交或完成,而是对应硬件层面的状态变化,比如打印机离线/在线切换、缺纸、卡纸、墨粉耗尽、设备被暂停或恢复等场景。普通的作业流转(如作业开始打印、完成)属于作业状态范畴,不会触发打印机状态的通知。打印机类型与驱动的影响
这确实和打印机类型及驱动实现直接相关:- 物理打印机:主流品牌的现代驱动会完整实现状态上报,但老旧驱动或第三方兼容性差的驱动可能遗漏部分状态通知逻辑。
- 虚拟打印机(如PDF打印机、虚拟传真机):这类设备没有实体硬件状态,驱动通常不会发送打印机状态变更通知,除非特意模拟相关状态。
- 网络打印机:依赖打印机的网络管理协议(如SNMP)和驱动的适配,如果打印机本身不支持状态上报,或驱动未实现该功能,也无法收到通知。
代码优化建议
你的代码中FindFirstPrinterChangeNotification的第二个参数(fdwFilter)传了0,虽然通过TPrinterNotifyOptions指定了监控字段,但部分驱动需要明确的过滤器标志才能触发通知。建议修改为:
hPrintNotify := FindFirstPrinterChangeNotification(hPrinter, PRINTER_CHANGE_STATUS, 0, @pno);
另外,可以先调用GetPrinter获取打印机当前的PRINTER_INFO_2结构中的Status字段,确认目标打印机是否存在可触发状态变更的场景(比如手动让打印机离线),以此验证通知机制是否正常。
内容的提问来源于stack exchange,提问作者yonni
相关产品推荐
相关产品推荐

