Catch events from printer in Windows

Viewed 540

The printer has a print queue where are documents prepared for printing that were sent to the printer from a few computers.

I'd like to write a code in Python that runs on the server and checks for printer events. Especially when the document has finished successfully printing, I'd like to catch this event and get information about the document

  • document name
  • number of pages
  • format (A4, A3, etc.)
  • was it colorful or black and white
  • time of finish printing

Could you help me bounce off?

I already studied this question but I can't figure out what I need from it.

I tried this code but ends with error message:

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
2 Answers

[GitHub]: ActiveState/code - (master) code/recipes/Python/305690_Enumerate_printer_job/recipe-305690.py (that raises ctypes.ArgumentError in your case) is ancient, some parts of it only worked by luck, and some of them never worked (as the flow didn't reach them).
I submitted [GitHub]: ActiveState/code - Fixes and updates which addresses the major problems (there are some left, but the code is working).

Starting from that, I tried to tailor it for this question, and address the items (partially at least). Since the question is tagged for [GitHub]: mhammond/pywin32 - pywin32, I used it in order to have (much) shorter (and Python friendlier) code.

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)

Output:

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

Grapically:

Img0

Notes:

The problem with the code sample is that it is expecting

#164: FirstJob = c_ulong(0) #Start from this job

But the verion of the ws = WinDLL("winspool.drv") you are using expects an Int per the error message.

My recommendation is to use something like C# as handling this kind of thing is easier and better documentented.

https://www.codeproject.com/Articles/51085/Monitor-jobs-in-a-printer-queue-NET

Related