diff --git a/contrib/monitordemo/build.xml b/contrib/monitordemo/build.xml index 79d2322830..cb5ad9e77f 100644 --- a/contrib/monitordemo/build.xml +++ b/contrib/monitordemo/build.xml @@ -9,7 +9,7 @@ - + diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Winspool.java b/contrib/platform/src/com/sun/jna/platform/win32/Winspool.java index cf66139e26..7dcdac4cb4 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Winspool.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Winspool.java @@ -28,6 +28,7 @@ import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Structure.FieldOrder; +import com.sun.jna.Union; import com.sun.jna.platform.win32.WinBase.SYSTEMTIME; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinDef.DWORDByReference; @@ -36,6 +37,7 @@ import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; @@ -151,6 +153,72 @@ public interface Winspool extends StdCallLibrary { public static final int PRINTER_ENUM_ICON8 = 0x00800000; public static final int PRINTER_ENUM_HIDE = 0x01000000; + public static final int PRINTER_NOTIFY_OPTIONS_REFRESH = 0x01; + + public static final int PRINTER_NOTIFY_INFO_DISCARDED = 0x01; + + public static final int PRINTER_NOTIFY_TYPE = 0x00; + public static final int JOB_NOTIFY_TYPE = 0x01; + + public static final short PRINTER_NOTIFY_FIELD_SERVER_NAME = 0x00; + public static final short PRINTER_NOTIFY_FIELD_PRINTER_NAME = 0x01; + public static final short PRINTER_NOTIFY_FIELD_SHARE_NAME = 0x02; + public static final short PRINTER_NOTIFY_FIELD_PORT_NAME = 0x03; + public static final short PRINTER_NOTIFY_FIELD_DRIVER_NAME = 0x04; + public static final short PRINTER_NOTIFY_FIELD_COMMENT = 0x05; + public static final short PRINTER_NOTIFY_FIELD_LOCATION = 0x06; + public static final short PRINTER_NOTIFY_FIELD_DEVMODE = 0x07; + public static final short PRINTER_NOTIFY_FIELD_SEPFILE = 0x08; + public static final short PRINTER_NOTIFY_FIELD_PRINT_PROCESSOR = 0x09; + public static final short PRINTER_NOTIFY_FIELD_PARAMETERS = 0x0A; + public static final short PRINTER_NOTIFY_FIELD_DATATYPE = 0x0B; + public static final short PRINTER_NOTIFY_FIELD_SECURITY_DESCRIPTOR = 0x0C; + public static final short PRINTER_NOTIFY_FIELD_ATTRIBUTES = 0x0D; + public static final short PRINTER_NOTIFY_FIELD_PRIORITY = 0x0E; + public static final short PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY = 0x0F; + public static final short PRINTER_NOTIFY_FIELD_START_TIME = 0x10; + public static final short PRINTER_NOTIFY_FIELD_UNTIL_TIME = 0x11; + public static final short PRINTER_NOTIFY_FIELD_STATUS = 0x12; + public static final short PRINTER_NOTIFY_FIELD_STATUS_STRING = 0x13; + public static final short PRINTER_NOTIFY_FIELD_CJOBS = 0x14; + public static final short PRINTER_NOTIFY_FIELD_AVERAGE_PPM = 0x15; + public static final short PRINTER_NOTIFY_FIELD_TOTAL_PAGES = 0x16; + public static final short PRINTER_NOTIFY_FIELD_PAGES_PRINTED = 0x17; + public static final short PRINTER_NOTIFY_FIELD_TOTAL_BYTES = 0x18; + public static final short PRINTER_NOTIFY_FIELD_BYTES_PRINTED = 0x19; + public static final short PRINTER_NOTIFY_FIELD_OBJECT_GUID = 0x1A; + public static final short PRINTER_NOTIFY_FIELD_FRIENDLY_NAME = 0x1B; + public static final short PRINTER_NOTIFY_FIELD_BRANCH_OFFICE_PRINTING = 0x1C; + + public static final short JOB_NOTIFY_FIELD_PRINTER_NAME = 0x00; + public static final short JOB_NOTIFY_FIELD_MACHINE_NAME = 0x01; + public static final short JOB_NOTIFY_FIELD_PORT_NAME = 0x02; + public static final short JOB_NOTIFY_FIELD_USER_NAME = 0x03; + public static final short JOB_NOTIFY_FIELD_NOTIFY_NAME = 0x04; + public static final short JOB_NOTIFY_FIELD_DATATYPE = 0x05; + public static final short JOB_NOTIFY_FIELD_PRINT_PROCESSOR = 0x06; + public static final short JOB_NOTIFY_FIELD_PARAMETERS = 0x07; + public static final short JOB_NOTIFY_FIELD_DRIVER_NAME = 0x08; + public static final short JOB_NOTIFY_FIELD_DEVMODE = 0x09; + public static final short JOB_NOTIFY_FIELD_STATUS = 0x0A; + public static final short JOB_NOTIFY_FIELD_STATUS_STRING = 0x0B; + public static final short JOB_NOTIFY_FIELD_SECURITY_DESCRIPTOR = 0x0C; + public static final short JOB_NOTIFY_FIELD_DOCUMENT = 0x0D; + public static final short JOB_NOTIFY_FIELD_PRIORITY = 0x0E; + public static final short JOB_NOTIFY_FIELD_POSITION = 0x0F; + public static final short JOB_NOTIFY_FIELD_SUBMITTED = 0x10; + public static final short JOB_NOTIFY_FIELD_START_TIME = 0x11; + public static final short JOB_NOTIFY_FIELD_UNTIL_TIME = 0x12; + public static final short JOB_NOTIFY_FIELD_TIME = 0x13; + public static final short JOB_NOTIFY_FIELD_TOTAL_PAGES = 0x14; + public static final short JOB_NOTIFY_FIELD_PAGES_PRINTED = 0x15; + public static final short JOB_NOTIFY_FIELD_TOTAL_BYTES = 0x16; + public static final short JOB_NOTIFY_FIELD_BYTES_PRINTED = 0x17; + public static final short JOB_NOTIFY_FIELD_REMOTE_JOB_ID = 0x18; + + public static final int PRINTER_NOTIFY_CATEGORY_ALL = 0x001000; + public static final int PRINTER_NOTIFY_CATEGORY_3D = 0x002000; + /** * The EnumPrinters function enumerates available printers, print servers, * domains, or print providers. @@ -565,6 +633,313 @@ boolean OpenPrinter( */ boolean ClosePrinter(HANDLE hPrinter); + /** + * The PRINTER_NOTIFY_OPTIONS structure specifies options for a change + * notification object that monitors a printer or print server. + * + * @see + * + * PRINTER_NOTIFY_OPTIONS structure + * + */ + @Structure.FieldOrder({ "Version", "Flags", "Count", "pTypes" }) + public class PRINTER_NOTIFY_OPTIONS extends Structure { + + /** + * The version of this structure. Set this member to 2. + */ + public int Version = 2; + + /** + * A bit flag. If you set the PRINTER_NOTIFY_OPTIONS_REFRESH flag in a + * call to the FindNextPrinterChangeNotification function, the function + * provides current data for all monitored printer information fields. + * The FindFirstPrinterChangeNotification function ignores the Flags + * member. + */ + public int Flags; + + /** + * The number of elements in the pTypes array. + */ + public int Count; + + /** + * A pointer to an array of PRINTER_NOTIFY_OPTIONS_TYPE structures. Use + * one element of this array to specify the printer information fields + * to monitor, and one element to specify the job information fields to + * monitor. You can monitor either printer information, job + * information, or both. + */ + public PRINTER_NOTIFY_OPTIONS_TYPE.ByReference pTypes; + + } + + /** + * The PRINTER_NOTIFY_OPTIONS_TYPE structure specifies the set of printer + * or job information fields to be monitored by a printer change + * notification object. + * + * @see + * + * PRINTER_NOTIFY_OPTIONS_TYPE structure + * + */ + @Structure.FieldOrder({ "Type", "Reserved0", "Reserved1", "Reserved2", + "Count", "pFields" }) + public class PRINTER_NOTIFY_OPTIONS_TYPE extends Structure { + + public static class ByReference extends PRINTER_NOTIFY_OPTIONS_TYPE + implements Structure.ByReference { + } + + /** + * The type to be watched. + */ + public short Type; + + /** + * Reserved. + */ + public short Reserved0; + + /** + * Reserved. + */ + public int Reserved1; + + /** + * Reserved. + */ + public int Reserved2; + + /** + * The number of elements in the pFields array. + */ + public int Count; + + /** + * A pointer to an array of values. Each element of the array specifies + * a job or printer information field of interest. + */ + public Pointer pFields; + + public void setFields(short[] fields) { + final long shortSizeInBytes = 2L; + Memory fieldsMemory = new Memory(fields.length * shortSizeInBytes); + fieldsMemory.write(0, fields, 0, fields.length); + pFields = fieldsMemory; + Count = fields.length; + } + + public short[] getFields() { + return pFields.getShortArray(0, Count); + } + } + + /** + * The PRINTER_NOTIFY_INFO structure contains printer information returned + * by the FindNextPrinterChangeNotification function. The function returns + * this information after a wait operation on a printer change notification + * object has been satisfied. + * + * @see + * + * PRINTER_NOTIFY_INFO structure + * + */ + @Structure.FieldOrder({ "Version", "Flags", "Count", "aData" }) + public class PRINTER_NOTIFY_INFO extends Structure { + + /** + * The version of this structure. Set this member to 2. + */ + public int Version; + + /** + * A bit flag that indicates the state of the notification structure. If + * the PRINTER_NOTIFY_INFO_DISCARDED bit is set, it indicates that some + * notifications had to be discarded. + */ + public int Flags; + + /** + * The number of PRINTER_NOTIFY_INFO_DATA elements in the aData array. + */ + public int Count; + + /** + * An array of PRINTER_NOTIFY_INFO_DATA structures. Each element of the + * array identifies a single job or printer information field, and + * provides the current data for that field. + */ + public PRINTER_NOTIFY_INFO_DATA[] aData = + new PRINTER_NOTIFY_INFO_DATA[1]; + + @Override + public void read() { + int count = (Integer) readField("Count"); + aData = new PRINTER_NOTIFY_INFO_DATA[count]; + if (count == 0) { + Count = count; + Version = (Integer) readField("Version"); + Flags = (Integer) readField("Flags"); + } else { + super.read(); + } + } + + } + + /** + * A struct containing non-numeric notification data - conditional content + * of a {@link NOTIFY_DATA} union. + */ + @Structure.FieldOrder({ "cbBuf", "pBuf" }) + public class NOTIFY_DATA_DATA extends Structure { + + /** + * Indicates the size, in bytes, of the buffer pointed to by pBuf. + */ + public int cbBuf; + + /** + * Pointer to a buffer that contains the field's current data. + */ + public Pointer pBuf; + + } + + /** + * A union of data information based on the Type and Field members of + * {@link PRINTER_NOTIFY_INFO_DATA} + */ + public class NOTIFY_DATA extends Union { + + /** + * Set if the notification data is numeric. + * + * An array of two DWORD values. For information fields that use only a + * single DWORD, the data is in adwData [0]. + */ + public int[] adwData = new int[2]; + + /** + * Set if the notification data is non-numeric. + */ + public NOTIFY_DATA_DATA Data; + + } + + /** + * The PRINTER_NOTIFY_INFO_DATA structure identifies a job or printer + * information field and provides the current data for that field. + * + * @see + * + * PRINTER_NOTIFY_INFO_DATA structure + * + */ + @Structure.FieldOrder({ "Type", "Field", "Reserved", "Id", "NotifyData" }) + public class PRINTER_NOTIFY_INFO_DATA extends Structure { + + /** + * Indicates the type of information provided. + */ + public short Type; + + /** + * Indicates the field that changed. + */ + public short Field; + + /** + * Reserved. + */ + public int Reserved; + + /** + * Indicates the job identifier if the Type member specifies + * JOB_NOTIFY_TYPE. If the Type member specifies PRINTER_NOTIFY_TYPE, + * this member is undefined. + */ + public int Id; + + /** + * A union of data information based on the Type and Field members. + */ + public NOTIFY_DATA NotifyData; + + @Override + public void read() { + super.read(); + + boolean numericData; + if (Type == PRINTER_NOTIFY_TYPE) { + switch (Field) { + case PRINTER_NOTIFY_FIELD_ATTRIBUTES: + // Fall-through + case PRINTER_NOTIFY_FIELD_PRIORITY: + // Fall-through + case PRINTER_NOTIFY_FIELD_DEFAULT_PRIORITY: + // Fall-through + case PRINTER_NOTIFY_FIELD_START_TIME: + // Fall-through + case PRINTER_NOTIFY_FIELD_UNTIL_TIME: + // Fall-through + case PRINTER_NOTIFY_FIELD_STATUS: + // Fall-through + case PRINTER_NOTIFY_FIELD_CJOBS: + // Fall-through + case PRINTER_NOTIFY_FIELD_AVERAGE_PPM: + numericData = true; + default: + numericData = false; + } + } else { + switch (Field) { + case JOB_NOTIFY_FIELD_STATUS: + // Fall-through + case JOB_NOTIFY_FIELD_PRIORITY: + // Fall-through + case JOB_NOTIFY_FIELD_POSITION: + // Fall-through + case JOB_NOTIFY_FIELD_START_TIME: + // Fall-through + case JOB_NOTIFY_FIELD_UNTIL_TIME: + // Fall-through + case JOB_NOTIFY_FIELD_TIME: + // Fall-through + case JOB_NOTIFY_FIELD_TOTAL_PAGES: + // Fall-through + case JOB_NOTIFY_FIELD_PAGES_PRINTED: + // Fall-through + case JOB_NOTIFY_FIELD_TOTAL_BYTES: + // Fall-through + case JOB_NOTIFY_FIELD_BYTES_PRINTED: + numericData = true; + default: + numericData = false; + } + } + if (numericData) { + NotifyData.setType(int[].class); + } else { + NotifyData.setType(NOTIFY_DATA_DATA.class); + } + NotifyData.read(); + } + } + + @Deprecated + HANDLE FindFirstPrinterChangeNotification( + // _In_ + HANDLE hPrinter, + int fdwFilter, + int fdwOptions, + // _In_opt_ + LPVOID pPrinterNotifyOptions); + /** * The FindFirstPrinterChangeNotification function creates a change * notification object and returns a handle to the object. You can then use @@ -614,9 +989,22 @@ boolean OpenPrinter( */ HANDLE FindFirstPrinterChangeNotification( // _In_ - HANDLE hPrinter, int fdwFilter, int fdwOptions, + HANDLE hPrinter, + int fdwFilter, + int fdwOptions, // _In_opt_ - LPVOID pPrinterNotifyOptions); + PRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions); + + @Deprecated + boolean FindNextPrinterChangeNotification( + // _In_ + HANDLE hChange, + // _Out_opt_ + DWORDByReference pdwChange, + // _In_opt_ + LPVOID pPrinterNotifyOptions, + // _Out_opt_ + LPVOID ppPrinterNotifyInfo); /** * The FindNextPrinterChangeNotification function retrieves information @@ -689,9 +1077,9 @@ boolean FindNextPrinterChangeNotification( // _Out_opt_ DWORDByReference pdwChange, // _In_opt_ - LPVOID pPrinterNotifyOptions, + PRINTER_NOTIFY_OPTIONS pPrinterNotifyOptions, // _Out_opt_ - LPVOID ppPrinterNotifyInfo); + PointerByReference ppPrinterNotifyInfo); /** * The FindClosePrinterChangeNotification function closes a change @@ -716,6 +1104,27 @@ boolean FindClosePrinterChangeNotification( // _In_ HANDLE hChange); + /** + * The FreePrinterNotifyInfo function frees a system-allocated buffer + * created by the FindNextPrinterChangeNotification function. + * + * @param pPrinterNotifyInfo + * [in] Pointer to a PRINTER_NOTIFY_INFO buffer returned from a + * call to the FindNextPrinterChangeNotification function. + * FreePrinterNotifyInfo deallocates this buffer. + * + * @return If the function succeeds, the return value is a nonzero value. If + * the function fails, the return value is zero. + * + * @see + * + * FreePrinterNotifyInfo function + * + */ + boolean FreePrinterNotifyInfo( + // _In_ + Pointer pPrinterNotifyInfo); + /** * The EnumJobs function retrieves information about a specified set of * print jobs for a specified printer. diff --git a/contrib/w32printing/build.xml b/contrib/w32printing/build.xml index 296d8ca227..5ac1fca8a8 100644 --- a/contrib/w32printing/build.xml +++ b/contrib/w32printing/build.xml @@ -9,7 +9,7 @@ - + diff --git a/contrib/w32printing/src/com/sun/jna/platform/win32/Win32SpoolMonitor.java b/contrib/w32printing/src/com/sun/jna/platform/win32/Win32SpoolMonitor.java index 8a006746d1..5cc6217a46 100644 --- a/contrib/w32printing/src/com/sun/jna/platform/win32/Win32SpoolMonitor.java +++ b/contrib/w32printing/src/com/sun/jna/platform/win32/Win32SpoolMonitor.java @@ -22,27 +22,38 @@ */ package com.sun.jna.platform.win32; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.text.DateFormat; +import com.sun.jna.Structure; import com.sun.jna.platform.win32.WinBase.FILETIME; import com.sun.jna.platform.win32.WinBase.SYSTEMTIME; import com.sun.jna.platform.win32.WinDef.DWORDByReference; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; -import com.sun.jna.platform.win32.Winspool.JOB_INFO_1; +import com.sun.jna.platform.win32.Winspool.*; +import com.sun.jna.ptr.PointerByReference; + +import static com.sun.jna.platform.win32.Winspool.*; public class Win32SpoolMonitor { - public Win32SpoolMonitor() { + private static final int TWO_DIMENSIONAL_PRINTERS = 0; + + private static final Charset UTF_16LE = Charset.forName("UTF-16LE"); - String pPrinterName = "HP Color LaserJet CM4730 MFP PCL 6"; + public void monitorPrinter(String pPrinterName) { HANDLEByReference phPrinter = new HANDLEByReference(); Winspool.INSTANCE.OpenPrinter(pPrinterName, phPrinter, null); // Get change notification handle for the printer - HANDLE chgObject = Winspool.INSTANCE - .FindFirstPrinterChangeNotification(phPrinter.getValue(), - Winspool.PRINTER_CHANGE_JOB, 0, null); + HANDLE chgObject = Winspool.INSTANCE.FindFirstPrinterChangeNotification( + phPrinter.getValue(), + Winspool.PRINTER_CHANGE_JOB, + 0, + (PRINTER_NOTIFY_OPTIONS) null); if (chgObject != null) { while (true) { @@ -51,9 +62,12 @@ public Win32SpoolMonitor() { WinBase.INFINITE); DWORDByReference pdwChange = new DWORDByReference(); - boolean fcnreturn = Winspool.INSTANCE - .FindNextPrinterChangeNotification(chgObject, - pdwChange, null, null); + boolean fcnreturn = + Winspool.INSTANCE.FindNextPrinterChangeNotification( + chgObject, + pdwChange, + (PRINTER_NOTIFY_OPTIONS) null, + (PointerByReference) null); if (fcnreturn) { JOB_INFO_1[] jobInfo1 = WinspoolUtil.getJobInfo1(phPrinter); @@ -80,6 +94,120 @@ public int getLastError() { return rc; } + public void monitorAllPrinters() { + System.out.println("Monitoring all printers, press Ctrl + C to stop"); + HANDLEByReference printServerHandle = new HANDLEByReference(); + boolean success = + Winspool.INSTANCE.OpenPrinter(null, printServerHandle, null); + if (!success) { + int errorCode = Kernel32.INSTANCE.GetLastError(); + throw new RuntimeException("Failed to access the print server - " + + errorCode); + } + + try { + PRINTER_NOTIFY_OPTIONS options = new PRINTER_NOTIFY_OPTIONS(); + options.Count = 1; + PRINTER_NOTIFY_OPTIONS_TYPE.ByReference optionsType = + new PRINTER_NOTIFY_OPTIONS_TYPE.ByReference(); + optionsType.Type = JOB_NOTIFY_TYPE; + optionsType.setFields(new short[] { + JOB_NOTIFY_FIELD_PRINTER_NAME, + JOB_NOTIFY_FIELD_STATUS, + JOB_NOTIFY_FIELD_DOCUMENT + }); + optionsType.toArray(1); + options.pTypes = optionsType; + HANDLE changeNotificationsHandle = + Winspool.INSTANCE.FindFirstPrinterChangeNotification( + printServerHandle.getValue(), + PRINTER_CHANGE_ADD_JOB | + PRINTER_CHANGE_SET_JOB | + PRINTER_CHANGE_DELETE_JOB, + TWO_DIMENSIONAL_PRINTERS, + options); + if (!isValidHandle(changeNotificationsHandle)) { + int errorCode = Kernel32.INSTANCE.GetLastError(); + throw new RuntimeException("Failed to get a change handle - " + + errorCode); + } + + try { + while (true) { + Kernel32.INSTANCE.WaitForSingleObject( + changeNotificationsHandle, + WinBase.INFINITE); + + DWORDByReference change = + new DWORDByReference(); + PointerByReference infoPointer = new PointerByReference(); + success = + Winspool.INSTANCE.FindNextPrinterChangeNotification( + changeNotificationsHandle, + change, + options, + infoPointer); + if (!success) { + int errorCode = Kernel32.INSTANCE.GetLastError(); + throw new RuntimeException("Failed to get printer " + + "change notification - " + errorCode); + } + + System.out.println("Change - " + + String.format("0x%08X", change.getValue().longValue())); + + if (infoPointer.getValue() != null) { + PRINTER_NOTIFY_INFO info = + Structure.newInstance(PRINTER_NOTIFY_INFO.class, + infoPointer.getValue()); + info.read(); + + try { + if ((info.Flags & PRINTER_NOTIFY_INFO_DISCARDED) > 0) { + System.out.println("Some information was " + + "discarded"); + } + + for (PRINTER_NOTIFY_INFO_DATA data : info.aData) { + System.out.println("Job ID - " + data.Id); + if (data.Field == JOB_NOTIFY_FIELD_PRINTER_NAME) { + String printerName = new String( + data.NotifyData.Data.pBuf.getByteArray( + 0, + data.NotifyData.Data.cbBuf), + UTF_16LE); + System.out.println("Printer - " + printerName); + } else if (data.Field == JOB_NOTIFY_FIELD_STATUS) { + System.out.println("Status - " + + String.format("0x%08X", data.NotifyData.adwData[0])); + } else { + String jobName = new String( + data.NotifyData.Data.pBuf.getByteArray( + 0, + data.NotifyData.Data.cbBuf), + UTF_16LE); + System.out.println("Job Name - " + jobName); + } + } + } finally { + Winspool.INSTANCE.FreePrinterNotifyInfo(info.getPointer()); + } + } + System.out.println("=================================================="); + } + } finally { + Winspool.INSTANCE.FindClosePrinterChangeNotification( + changeNotificationsHandle); + } + } finally { + Winspool.INSTANCE.ClosePrinter(printServerHandle.getValue()); + } + } + + private boolean isValidHandle(HANDLE handle) { + return handle != null && !handle.equals(Kernel32.INVALID_HANDLE_VALUE); + } + private void printJobInfo(JOB_INFO_1 jobInfo1) { FILETIME lpFileTime = new FILETIME(); Kernel32.INSTANCE.SystemTimeToFileTime(jobInfo1.Submitted, lpFileTime); @@ -102,7 +230,17 @@ private void printJobInfo(JOB_INFO_1 jobInfo1) { /** * @param args */ - public static void main(String[] args) { - new Win32SpoolMonitor(); + public static void main(String[] args) throws Exception { + System.out.print("Please enter the name of a printer to monitor, " + + "or press enter to monitor all printers: "); + BufferedReader reader = new BufferedReader( + new InputStreamReader(System.in)); + String printer = reader.readLine(); + Win32SpoolMonitor monitor = new Win32SpoolMonitor(); + if (printer.isEmpty()) { + monitor.monitorAllPrinters(); + } else { + monitor.monitorPrinter(printer); + } } }