【问题标题】:Catch events from printer in Windows在 Windows 中从打印机捕获事件
【发布时间】:2021-11-24 21:39:06
【问题描述】:

打印机有一个打印队列,其中准备打印的文档从几台计算机发送到打印机。

我想在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

【问题讨论】:

  • 从该链接看来,您至少可以获取文档名称以及打印开始和结束时间。但我认为,如果没有现有的简单解决方案来获取其他信息,并且您仍然坚持必须用 Python 编写(而不是与 Python 脚本协同工作的外部程序),您将可能必须使用ctypes 库并深入研究relevant Windows SDK。您是否至少尝试过链接中的解决方案并对其进行调试以查看是否有更多信息可用?
  • 好的,只是在发帖之前你应该自己做尽可能多的事情:meta.stackoverflow.com/q/261592/6273251
  • @xralf 每个计算机系统都有自己的打印机驱动程序,但打印机在网络上,因为它是 Oce VarioPrint 4120。所以你打算部署一个 python 脚本到每个可以打印到相关打印机的计算机?
  • @xralf 试试this exampel,看看它是否满足您的需求。
  • @xralf 我认为您在作业被发送到打印机之后不会得到信息。相反,您需要编写一个为您设置工作的程序,但该任务将花费更多时间,因为您可能希望花在它上面。根据我对文档的理解,您只能访问 SpoolerApi 并且您可以获取的信息是有限的。为什么不使用文件名来考虑会发生什么?看起来一个标准的文件名就足够了。

标签: python python-3.x events printing pywin32


【解决方案1】:

[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.

图形化:

注意事项

【讨论】:

  • 我不确定如何(或是否有可能)获得所有这些。有一种方法可以get all 的打印机。 MSDN
  • @Thingamabobs:这样,我很确定这是不可能的。我有一台 Win 机器(我在上面试一下)和另一台 OSX。连接到同一台打印机,它们的队列非常独立。但是“打印服务器”牌还没有打出。
  • @Thingamabobs:我刚刚意识到您的评论是关于什么的:这不是关于(而且从来不是关于)所有注册的打印机一个系统。那是一个不同的故事。作为记录,我安装了 20 多个打印机驱动程序(并且没有一个对应于 active 打印机)。
  • 由于我没有在这里工作的环境,我真的只是假设应该可以使用EnumPrinters 并使用GetPrinterPRINTER_INFO_6。当然,打印机首先需要可用于请求这些信息的 PC,但这不是问题的一部分。
  • 是的,但这是一个不同的级别。我也和那些人合作过。 stackoverflow.com/questions/44109985/…EnumPrinters 返回的每条记录都可以有自己的队列。我正在使用默认打印机,“巧合”在其队列中有一些作业。
【解决方案2】:

代码示例的问题在于它是预期的

#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

【讨论】:

  • 指示的参数(和代码行)不正确。
猜你喜欢
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-09
  • 2012-05-05
相关资源
最近更新 更多