在Windows中捕获打印机事件

4
打印机有一个打印队列,其中包含从几台计算机发送到打印机的准备好打印的文档。
我想编写一段在服务器上运行的Python代码,用于检查打印机事件。特别是当文档成功打印完成时,我想捕获此事件并获取有关文档的信息:
- 文档名称 - 页数 - 格式(A4、A3等) - 是否为彩色或黑白 - 完成打印的时间
你能帮我解决这个问题吗?
我已经阅读了this question,但我无法确定我需要什么。
我尝试了this code但出现错误消息:
Traceback (most recent call last):
  File "...recipe-305690-1.py", line 195, in <module>
    prt.EnumJobs(pJob, prt.pcbNeeded)
  File "...recipe-305690-1.py", line 164, in EnumJobs
    ret = ws.EnumJobsA(self.OpenPrinter(),
ctypes.ArgumentError: argument 5: <class 'OverflowError'>: int too long to convert

1
从那个链接看起来,你至少可以获取文档名称和打印开始和结束时间。但是我认为,如果没有现成的简单解决方案来获取其他信息,并且你仍然坚持必须用Python编写(而不是与Python脚本配合使用的外部程序),你可能需要使用ctypes库并深入研究相关的Windows SDK。你是否至少尝试过链接中的解决方案并调试它以查看是否有更多信息可用? - Random Davis
2
好的,这只是说在发布到这里之前,您应该尽可能地自己完成所有工作:https://meta.stackoverflow.com/q/261592/6273251 - Random Davis
2
@xralf 试试这个例子,看看它是否满足你的需求。 - Thingamabobs
1
@xralf,我认为你无法在作业发送到打印机之后获取信息。相反,你需要编写一个程序来为你设置作业,但这项任务需要比你想象中花费更多的时间。根据我对文档的理解,你只能访问SpoolerApi,并且你可以在那里获得的信息是有限的。为什么不使用文件名来考虑发生了什么?标准文件名似乎已经足够了。 - Thingamabobs
1
查看这些问题以解决该错误信息,该错误似乎与 Windows 64 位系统相关,而不是 Windows 32 位系统。链接为 https://www.google.com/search?q=types.ArgumentError:+argument+5:+%3Cclass+%27OverflowError%27%3E:+site:stackoverflow.com - Life is complex
显示剩余23条评论
2个回答

2

[GitHub]: ActiveState/code - (master) code/recipes/Python/305690_Enumerate_printer_job/recipe-305690.py(在您的情况下引发ctypes.ArgumentError)过于陈旧,其中一些部分仅仅是凭运气运行成功的,而有些甚至从未工作过(因为流程从未到达这些部分)。
我提交了[GitHub]: ActiveState/code - Fixes and updates,解决了主要问题(还有一些问题未解决,但代码可以运行)。

基于此,我尝试为此问题量身定做,并针对其中的某些部分进行了修改。由于该问题被标记为[GitHub]: mhammond/pywin32 - pywin32,因此我使用了它,以便编写更短且更友好(用Python语言)的代码。

code00.py:

#!/usr/bin/env python

import sys
import time
import win32con as wcon
import win32print as wprn
from pprint import pprint as pp


JOB_INFO_RAW_KEYS_DICT = {  # Can comment uninteresting ones
    "JobId": "Id",
    "Position": "Index",
    "pPrinterName": "Printer",
    "pUserName": "User",
    "pDocument": "Document",
    "TotalPages": "Pages",
    "Submitted": "Created",
}


def generate_constant_strings(header, mod=None):
    header_len = len(header)
    ret = {}
    for k, v in (globals() if mod is None else mod.__dict__).items():
        if k.startswith(header):
            ret[v] = k[header_len:].capitalize()
    return ret


JOBSTATUS_DICT = generate_constant_strings("JOB_STATUS_", mod=wcon)
DMCOLOR_DICT = generate_constant_strings("DMCOLOR_", mod=wcon)
DMPAPER_DICT = generate_constant_strings("DMPAPER_", mod=wcon)


def printer_jobs(name, level=2):
    p = wprn.OpenPrinter(wprn.GetDefaultPrinter() if name is None else name, None)
    jobs = wprn.EnumJobs(p, 0, -1, level)
    wprn.ClosePrinter(p)
    return jobs


def job_data(job, raw_keys_dict=JOB_INFO_RAW_KEYS_DICT):
    ret = {}
    for k, v in job.items():
        if k in raw_keys_dict:
            ret[raw_keys_dict[k]] = v
    ret["Format"] = DMPAPER_DICT.get(job["pDevMode"].PaperSize)
    ret["Color"] = DMCOLOR_DICT.get(job["pDevMode"].Color)
    ret["Status"] = JOBSTATUS_DICT.get(job["Status"])
    return ret


def main(*argv):
    printer_name = None
    interval = 3
    while 1:
        try:
            jobs = printer_jobs(printer_name)
            for job in jobs:
                data = job_data(job)
                pp(data, indent=2, sort_dicts=0)
            time.sleep(interval)
        except KeyboardInterrupt:
            break


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q070103258]> "e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts\python.exe" code00.py
Python 3.8.7 (tags/v3.8.7:6503f05, Dec 21 2020, 17:59:51) [MSC v.1928 64 bit (AMD64)] 064bit on win32

{ 'Id': 9,
  'Printer': 'WF-7610 Series(Network)',
  'User': 'cfati',
  'Document': '*Untitled - Notepad',
  'Index': 1,
  'Pages': 1,
  'Created': pywintypes.datetime(2021, 12, 3, 22, 52, 22, 923000, tzinfo=TimeZoneInfo('GMT Standard Time', True)),
  'Format': 'A4',
  'Color': 'Color',
  'Status': None}
{ 'Id': 13,
  'Printer': 'WF-7610 Series(Network)',
  'User': 'cfati',
  'Document': 'e:\\Work\\Dev\\StackOverflow\\q070103258\\code00.py',
  'Index': 2,
  'Pages': 4,
  'Created': pywintypes.datetime(2021, 12, 3, 23, 10, 40, 430000, tzinfo=TimeZoneInfo('GMT Standard Time', True)),
  'Format': 'A3',
  'Color': 'Monochrome',
  'Status': None}

Done.

图示:

Img0

注:


我不确定如何(或者是否可能)获取它们所有。有一种方法可以获取所有打印机MSDN - Thingamabobs
@Thingamabobs:我刚意识到你的评论的真正意图:它并不涉及(也从来没有涉及)系统上所有注册打印机。那是完全不同的故事。就记录而言,我安装了20多个打印机驱动程序(其中没有一个对应于活动打印机)。 - CristiFati
由于我没有环境来解决这个问题,我只是假设可以使用EnumPrinters并且使用GetPrinterPRINTER_INFO_6进行操作。当然,打印机需要对请求这些信息的计算机可用,但这不是问题的一部分。 - Thingamabobs
是的,但那是不同的层次。我也曾经使用过它们。https://stackoverflow.com/questions/44109985/how-can-i-use-setjob-in-win32print/44115745#44115745。*EnumPrinters*返回的每个记录都可以有自己的队列。我正在使用默认打印机,它“巧合地”在其队列中有一些作业。 - CristiFati
@xralf:非常感谢您的礼物(尤其是因为答案只部分回答了您的问题)!请告诉我它的效果如何! - CristiFati
显示剩余12条评论

1
代码示例存在的问题是它期望某些东西。
#164: FirstJob = c_ulong(0) #Start from this job

但是,根据错误信息,您正在使用的ws = WinDLL("winspool.drv")版本期望一个Int
我的建议是使用类似C#这样的语言,因为处理这种情况更容易,并且有更好的文档说明。
请参考此链接:https://www.codeproject.com/Articles/51085/Monitor-jobs-in-a-printer-queue-NET

指定的参数(和代码行)不正确。 - CristiFati

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接