Procedure to get locally installed printer information

This procedure will list information about locally installed printers via Microsoft’s Level 2 EnumPrinters API. It shows the information in the procedure logs. Using ctypes and EnumPrinters the need for extra dependencies is minimized though it may need .Net 4.0. The type of information retrieved can be added or changed to represent any of the values in the structure. Please see the comments in the procedure.

Here are the Windows MSDNs:

EnumPrinters MSDN
msdn.microsoft.com/en-us/library/windows/desktop/dd162692(v=vs.85).aspx

Level 2 PRINTER_INFO_2
msdn.microsoft.com/en-us/library/windows/desktop/dd162845(v=vs.85).aspx

Here is my exported procedure:

Here is a link to the procedure:


import ctypes
from ctypes.wintypes import BYTE, DWORD, LPCWSTR

winspool = ctypes.WinDLL('winspool.drv')  # short command substitution for EnumPrintersW since EnumPrinters cannot be directly used in Python.
msvcrt = ctypes.cdll.msvcrt  # for malloc, free.

# Parameters: modify as you need. See MSDN for detail.
PRINTER_ENUM_LOCAL = 2
PRINTER_ENUM_CONNECTIONS = 4
Name = None  # None equals NULL so PRINTER_ENUM_LOCAL only pulls locally installed pinters
Level = 2  # there are also 1, 4, 5

# Structure with all the datatypes and variables that are passed via the EnumPrintersW function. DO NOT REMOVE ANY! These seem to be all required as is or it breaks
class PRINTER_INFO_2(ctypes.Structure):
    _fields_ = [
        ("pServerName", LPCWSTR),
        ("pPrinterName", LPCWSTR),
        ("pShareName", LPCWSTR),
        ("pPortName", LPCWSTR),
        ("pDriverName", LPCWSTR),
        ("pComment", LPCWSTR),
        ("pLocation", LPCWSTR),
        ("pDevMode", LPCWSTR),
        ("pSepFile", LPCWSTR),
        ("pPrintProcessor", LPCWSTR),
        ("pDataType", LPCWSTR),
        ("pParameters", LPCWSTR),
        ("pSecurityDescriptor", LPCWSTR),
        ("Attributes", DWORD),
        ("Priority", DWORD),
        ("DefaultPriority", DWORD),
        ("StartTime", DWORD),
        ("UntilTime", DWORD),
        ("Status", DWORD),
        ("cJobs", DWORD),
        ("AveragePPM", DWORD),
    ]

# Invoke once with a NULL pointer to get buffer size required based on the number of printers it finds.
info = ctypes.POINTER(BYTE)()
pcbNeeded = DWORD(0)
pcReturned = DWORD(0)  
winspool.EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, Name, Level, ctypes.byref(info), 0,
        ctypes.byref(pcbNeeded), ctypes.byref(pcReturned))

bufsize = pcbNeeded.value    # The buffer size as determined by the NULL value run
buffer = msvcrt.malloc(bufsize)    # The number of PRINTER_INFO_2 structures retrieved

#    Gets the printer information and fills the structure
winspool.EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, Name, Level, buffer, bufsize,
        ctypes.byref(pcbNeeded), ctypes.byref(pcReturned))
info = ctypes.cast(buffer, ctypes.POINTER(PRINTER_INFO_2))
for i in range(pcReturned.value):
    print "Printer:",  info[i].pPrinterName, "	Driver:", info[i].pDriverName, "	Port:", info[i].pPortName    # Modify to show which values you want from the structure
msvcrt.free(buffer) # Deallocates the memoryblock after everything is finished running

Thank you!!!