Hi,
I'm trying to use
Winspool.FindFirstPrinterChangeNotification() and Winspool.FindNextPrinterChangeNotification(), and am attempting to pass in the options and retrieve data back from the calls.
public class Winspool2 {
@Structure.FieldOrder({ "Version", "Flags", "Count", "pTypes" })
public static class PRINTER_NOTIFY_OPTIONS extends Structure {
public static class ByReference extends PRINTER_NOTIFY_OPTIONS implements Structure.ByReference {
}
public int Version;
public int Flags;
public int Count;
public PRINTER_NOTIFY_OPTIONS_TYPE.ByReference pTypes;
}
@Structure.FieldOrder({ "Type", "Reserved0", "Reserved1", "Reserved2", "Count", "pFields" })
public static class PRINTER_NOTIFY_OPTIONS_TYPE extends Structure {
public static class ByReference extends PRINTER_NOTIFY_OPTIONS_TYPE implements Structure.ByReference {
}
public short Type;
public short Reserved0;
public int Reserved1;
public int Reserved2;
public int Count;
public Pointer pFields;
}
@Structure.FieldOrder({ "Version", "Flags", "Count", "aData" })
public static class PRINTER_NOTIFY_INFO extends Structure {
public static class ByReference extends PRINTER_NOTIFY_INFO implements Structure.ByReference {
}
public static class ByValue extends PRINTER_NOTIFY_INFO implements Structure.ByValue {
}
public int Version;
public int Flags;
public int Count;
public PRINTER_NOTIFY_INFO_DATA[] aData = new PRINTER_NOTIFY_INFO_DATA[1];
}
@Structure.FieldOrder({ "cbBuf", "pBuf" })
public static class Data_struct extends Structure {
public static class ByReference extends Data_struct implements Structure.ByReference {
}
public static class ByValue extends Data_struct implements Structure.ByValue {
}
public int cbBuf;
public WinDef.PVOID pBuf;
}
public static class NotifyData_union extends Union {
public static class ByReference extends NotifyData_union implements Structure.ByReference {
}
public static class ByValue extends NotifyData_union implements Structure.ByValue {
}
public int[] adwData = new int[2];
public Data_struct Data;
}
@Structure.FieldOrder({ "Type", "Field", "Reserved", "Id", "NotifyData" })
public static class PRINTER_NOTIFY_INFO_DATA extends Structure {
public static final short JOB_NOTIFY_FIELD_PRINTER_NAME = 0x00;
public static final short JOB_NOTIFY_FIELD_STATUS = 0x0A;
public static final short JOB_NOTIFY_FIELD_DOCUMENT = 0x0D;
public static class ByReference extends PRINTER_NOTIFY_INFO_DATA implements Structure.ByReference {
}
public static class ByValue extends PRINTER_NOTIFY_INFO_DATA implements Structure.ByValue {
}
public short Type;
public short Field;
public int Reserved;
public int Id;
public NotifyData_union NotifyData;
@Override
public void read() {
super.read();
if (Type == JOB_NOTIFY_FIELD_STATUS) {
NotifyData.setType(int[].class);
} else {
NotifyData.setType(Data_struct.class);
}
NotifyData.read();
}
}
}
My application code looks like this:
public class JnaTest {
private static final int PRINTER_NOTIFY_OPTIONS_REFRESH = 0x01;
private static final int JOB_NOTIFY_TYPE = 0x01;
private static final int TWO_DIMENSIONAL_PRINTERS = 0;
private static final int NUMBER_OF_BYTES_IN_WORD = 2;
public static void main(String[] args) throws Exception {
WinNT.HANDLEByReference printServerHandle = new WinNT.HANDLEByReference();
boolean success = Winspool.INSTANCE.OpenPrinter(null, printServerHandle, null); // Get job info for all printers
if (!success) {
int errorCode = Kernel32.INSTANCE.GetLastError();
throw new RuntimeException("Failed to access the print server - " + errorCode);
}
try {
Winspool2.PRINTER_NOTIFY_OPTIONS options = new Winspool2.PRINTER_NOTIFY_OPTIONS();
options.Version = 2;
options.Flags = PRINTER_NOTIFY_OPTIONS_REFRESH;
options.Count = 1;
Winspool2.PRINTER_NOTIFY_OPTIONS_TYPE.ByReference optionsType = new Winspool2.PRINTER_NOTIFY_OPTIONS_TYPE.ByReference();
optionsType.Type = JOB_NOTIFY_TYPE;
optionsType.Count = 3;
optionsType.pFields = new Memory(3 * NUMBER_OF_BYTES_IN_WORD);
optionsType.pFields.write(0,
new short[] { Winspool2.PRINTER_NOTIFY_INFO_DATA.JOB_NOTIFY_FIELD_PRINTER_NAME,
Winspool2.PRINTER_NOTIFY_INFO_DATA.JOB_NOTIFY_FIELD_STATUS,
Winspool2.PRINTER_NOTIFY_INFO_DATA.JOB_NOTIFY_FIELD_DOCUMENT
},
0,
3);
optionsType.toArray(1);
options.pTypes = optionsType;
options.write();
WinDef.LPVOID optionsPointer = new WinDef.LPVOID(options.getPointer());
WinNT.HANDLE changeNotificationsHandle =
Winspool.INSTANCE.FindFirstPrinterChangeNotification(printServerHandle.getValue(),
Winspool.PRINTER_CHANGE_ADD_JOB
| Winspool.PRINTER_CHANGE_SET_JOB
| Winspool.PRINTER_CHANGE_DELETE_JOB,
TWO_DIMENSIONAL_PRINTERS,
optionsPointer);
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);
WinDef.DWORDByReference change = new WinDef.DWORDByReference();
Winspool2.PRINTER_NOTIFY_INFO.ByReference info = new Winspool2.PRINTER_NOTIFY_INFO.ByReference();
success = Winspool.INSTANCE.FindNextPrinterChangeNotification(changeNotificationsHandle,
change,
optionsPointer,
new WinDef.LPVOID(info.getPointer()));
if (!success) {
int errorCode = Kernel32.INSTANCE.GetLastError();
throw new RuntimeException("Failed to get printer change notification - " + errorCode);
}
info.read();
System.out.println("Change - " + String.format("0x%08X", change.getValue().longValue()));
System.out.println(info.Version); // This should be printing 2, but I get a large number instead
}
} finally {
runIfValidHandle(changeNotificationsHandle, Winspool.INSTANCE::FindClosePrinterChangeNotification);
}
} finally {
runIfValidHandle(printServerHandle.getValue(), Winspool.INSTANCE::ClosePrinter);
}
}
static boolean isValidHandle(WinNT.HANDLE handle) {
return handle != null && !handle.equals(Kernel32.INVALID_HANDLE_VALUE);
}
static void runIfValidHandle(WinNT.HANDLE handle, Consumer<WinNT.HANDLE> consumer) {
if (isValidHandle(handle)) {
consumer.accept(handle);
}
}
}
If you send a print job to any printer (e.g. the built in Microsoft Print to PDF), the contents of info.Version is a very large number, rather than 2 as defined in the Windows documentation.
My usage of pointers and ByReference must be wrong, but I don't know how (I'm new to JNA).
Any advice would be greatly appreciated.
Thanks,
Ian