Използване на JNA (Java Native Access) за достъп до Microsoft Windows Native COM DLL

java

Приложение JNA (Java Native Access) в Java проекти за достъп до функционалност и обекти, така наречените "родни" библиотеки - COM DLL Microsoft Windows представлява голям интерес.
Основното предимство е намаляването на времето за разработване на проекта, ако цялата необходима функционалност вече се съдържа в някаква стандартна библиотека на Microsoft Windows или има трета страна COM DLL с необходимия набор от решения, или вече се използва от клиента COM-DLL на бизнес логиката. Също така е невъзможно да се надцени възможността за използване COM DLL, работа с обекти Microsoft .NET Framework, написани например в ° С #.
Второто, но не по-малко важно предимство е това, за разлика от предишната технология JNI (Java Native Interface), не е нужно да пишете библиотека за обвивки в C тук, което наистина е съмнително удоволствие.
Но първо нещата първо. Помислете за задачата, която е предизвикала изучаването на технологиите JNA.
За нуждите на проекта се изискваше да получите списък с принтери в системата, но и името на техните драйвери, тъй като името на принтера може да бъде зададено ръчно, каквото искате, дори "MySuperPuperPrinter", а проектът се нуждае от нещо по-надеждно и стабилно. Резултатът от решението, предложено от Java, се оказа грешен ...
Нека използваме библиотеката javax.print:

Моята система показва следния изход:
PrinterName = Samsung SCX-4 × 28 Series PCL6, PrinterMakeAndModel = null
PrinterName = Samsung CLX-3180 Series, PrinterMakeAndModel = null
PrinterName = PDF Creator, PrinterMakeAndModel = null
PrinterName = FinePrint, PrinterMakeAndModel = null
PrinterName = Факс, PrinterMakeAndModel = нула

Къде е информацията за модела на драйвера или принтера? Тя не е.
Нека видим какво казва Oracle за това:

За атрибути като
javax.print.attribute.standard.PrinterMakeAndModel
javax.print.attribute.standard.PrinterLocation
javax.print.attribute.standard.PrinterInfo
javax.print.PrintService.getAttribute трябва да връща значима информация, получена от ОС Windows, вместо празни низове.
Тази грешка може да бъде възпроизведена винаги.
ОЦЕНКА 18.02.2003
В Windows тази информация може да бъде извлечена от PRINTER_INFO_X структура.

Добре, нека проверим най-новата Java 1.7.0_17 - резултатът е същият, т.е. Проблемът от 2003 г. не е отстранен.

Е, добре, по принцип Java не е проектирана да знае всички тънкости на операционната система, на която работи. Следователно как Microsoft Windows се справя със своите принтери, някои услуги на Microsoft Windows трябва да знаят. Това е - това е библиотеката c: WindowsSystem32Winspool.drv и нейната функция EnumPrinters. Функцията изброява принтерите в системата и добавя информацията към структурата PRINTER_INFO_2.

За достъп до библиотеката Winspool.drv можете да използвате JNI - за това трябва да напишем черупка в C, h-файл от страната на JNI и т.н. За неспециалист в тази технология, доста трудоемък и отнемащ време процес, и дори относно трудностите при отстраняване на грешки, аз обикновено мълча.

Да видим какво може да ни предложи JNA.

Нека започнем с най-оригиналния пример Hello World, който съм виждал от разработчика на JNA.

Тези. достатъчно е да декларирате разширяване на интерфейс com.sun.jna.Library, декларирайте в него необходимия метод с име и подпис, съответстващи на функцията от библиотеката (в този случай това е библиотеката c: WindowsSystem32kernel32.dll и функционират Бипкане) и заредете библиотеката, използвайки този интерфейс с com.sun.jna.Native. Първият параметър е името на библиотеката, която може да съдържа пълния път към нея във файловата система, а вторият е класът на интерфейса, който се използва за свързване на интерфейса и библиотеката чрез отражение.

Впечатляващо е колко лесно е да чуете компютърния поздрав „Здравей, свят“ в морзовата азбука.

Също така интересни примери можете да намерите тук.

Сега да се върнем към проектната задача - да получим имената на принтерите и техните драйвери в Microsoft Windows. Както бе споменато по-горе, трябва да се свържете Winspool.drv и чрез неговата функция EnumPrinters Вземете набор от структури PRINTER_INFO_2 за всеки принтер в системата. За щастие JNA се погрижи за опаковането на „родните“ библиотечни структури и осигури клас com.sun.jna.Structure.

Тестовият код изглежда така:

Какво виждаме тук? Интерфейс Уинспул сега се разширява StdCallLibrary, и също така съдържа структурата PRINTER_INFO_2, наследница Структура. Това е необходимост, защото обадете се Native.loadLibrary през отражение изгражда карта на интерфейса и неговите структури и ако извадите тази структура от интерфейса, картата ще бъде грешна. Добре, ако имаше грешка, структурата просто щеше да бъде попълнена неправилно с метода EnumPrinters - например вместо името на принтера в полето pPrinterName ще има само първата буква от името.

Структурата PRINTER_INFO_2 има още няколко функции: той трябва да съдържа конструктор без параметри и да замени метода защитен списък getFieldOrder () - строга последователност и именуване на полета.

Това е всичко. След стартиране на теста получаваме списък с принтери и техните драйвери в конзолата: