如何监控打印机单页打印事件?解决Win32_PrintJob TotalPages计数不准问题
我之前折腾打印监控的时候也踩过Win32_PrintJob的坑——TotalPages这个属性其实是作业的总页数预估值,在打印过程中可能不会实时更新,甚至有些驱动一开始根本不会填充这个值,所以用来统计已打印页数肯定不准。要实现逐页监控打印事件,靠计数器累加才是靠谱的路子,这里给你两个可行的方案:
方案一:用WMI事件订阅实时监听打印页数变化
WMI的__InstanceModificationEvent可以帮你捕获Win32_PrintJob对象的属性变更,我们只需要关注PagesPrinted的增量变化就能准确统计已打印页数。这个方法实现起来简单,不用调用复杂的原生API。
举个C#的实现例子:
using System; using System.Management; class PrintPageMonitor { static void Main(string[] args) { // 构造WMI查询:每1秒检查一次,只监听PagesPrinted增加的作业变更 string wmiQuery = @"SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_PrintJob' AND TargetInstance.PagesPrinted > PreviousInstance.PagesPrinted"; var watcher = new ManagementEventWatcher(wmiQuery); watcher.EventArrived += OnPrintPageUpdated; Console.WriteLine("打印页面监控已启动...按任意键停止"); watcher.Start(); Console.ReadKey(); watcher.Stop(); } private static void OnPrintPageUpdated(object sender, EventArrivedEventArgs e) { var jobInstance = (ManagementBaseObject)e.NewEvent["TargetInstance"]; int currentPrinted = Convert.ToInt32(jobInstance["PagesPrinted"]); string jobId = jobInstance["JobId"].ToString(); string printerName = jobInstance["PrinterName"].ToString(); Console.WriteLine($"打印机 [{printerName}] 作业 {jobId} 已打印页数:{currentPrinted}"); // 在这里把currentPrinted同步到你的计数器,或者做后续业务处理 } }
注意事项:
- 运行程序时需要足够的权限:最好以管理员身份启动,否则可能无法访问WMI的打印事件
- 部分打印机驱动可能会批量更新
PagesPrinted(比如每打印5页才更新一次),这时候你需要根据增量来累加计数器,而不是只取当前值
方案二:调用Windows打印池原生API(更可靠)
如果WMI的监控不够灵敏,或者遇到某些特殊驱动不兼容的情况,可以直接调用Windows Print Spooler的原生API,比如FindFirstPrinterChangeNotification和GetJob来监听打印作业的页面推进事件。这个方法更底层,兼容性更好,但实现起来稍复杂。
核心思路是:
- 注册打印机的作业变更通知(监听
PRINTER_CHANGE_JOB事件) - 当收到通知时,调用
GetJob获取作业的最新信息,提取PagesPrinted字段 - 根据字段的变化来更新你的计数器
关键提醒
别再用TotalPages来统计已打印页数了!它的作用是告诉你这个作业一共要打多少页,而不是已经打了多少页。真正能反映打印进度的是PagesPrinted,通过监听它的变化才能得到准确的已打印页数。
内容的提问来源于stack exchange,提问作者Ezz Redfox




