Как я могу получить марку и модель принтера в Java?

На самом деле я работаю над приложением Java, которое совместно использует принтеры с сервером, и мне нужно это приложение, чтобы получить марку и модель принтеров, которые оно разделяет.

Я знаю, что этот вопрос задавали три или четыре раза, но, похоже, никто не нашел ответа.

Я пробовал этот код:

PrintService[] printServices = PrintServiceLookup.lookupPrintServices(null, null);

        for (PrintService printer : printServices){           
            System.out.println(printer.getDefaultAttributeValue(PrinterMakeAndModel.class));
            System.out.println(printer.getAttribute(PrinterURI.class));
        }

первая печать всегда возвращает строку null, а вторая получает NullPointerException.

Некоторые исследования привели меня на эту страницу: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4673400

Похоже, это известная «ошибка», и я не очень понимаю оценку.

Я думаю, что обходным путем было бы получить марку и модель, отправив запрос SNMP на принтеры, но я ничего не знаю о SNMP и не уверен, что есть одна команда SNMP для получения марки и модели. любого принтера.

Если у кого-нибудь есть идеи о том, как этого добиться, используя метод Java, отправив команды SNMP или что-то еще, что можно сделать в любой ОС, ваша помощь будет оценена по достоинству.

ИЗМЕНИТЬ:

Вот ссылка на тему, где задавали тот же вопрос:

ИЗМЕНИТЬ 2:

Решение:

Как я сказал в комментарии, я попытался получить марку и модель через SNMP, отправив на принтер OID «1.3.6.1.2.1.25.3.2.1.3.1». Кажется, это работает, но я не уверен, что это работает на любом принтере, использующем тот же OID, и может произойти сбой, если SNMP деактивирован на целевом принтере.

Поэтому я наконец решил получить имя драйвера, используя JNA и Winspool.drv. Часть этого уже была реализована в JNA, но мне пришлось добавить некоторые структуры и функции.

Вот ссылка на существующий WinspoolUtil.java и Winspool.java в JNA.

А вот код с моим личным обновлением этих двух классов.

Винспул:

import java.util.Arrays;
import java.util.List;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.INT_PTR;
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.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

public class WinspoolUpdate {
    public interface WinspoolLib extends StdCallLibrary {

        WinspoolLib INSTANCE = (WinspoolLib) Native.loadLibrary("Winspool.drv", WinspoolLib.class,
                W32APIOptions.UNICODE_OPTIONS);

        boolean EnumPrinters(int Flags, String Name, int Level, Pointer pPrinterEnum,
                int cbBuf, IntByReference pcbNeeded, IntByReference pcReturned);

        boolean GetPrinter(HANDLE hPrinter, int Level, Pointer pPrinter, int cbBuf, IntByReference pcbNeeded);

        boolean OpenPrinter(String pPrinterName, HANDLEByReference phPrinter, Pointer pDefault);

        public static class PRINTER_INFO_1 extends Structure {
            public int Flags;
            public String pDescription;
            public String pName;
            public String pComment;

            protected List<String> getFieldOrder() {
                return Arrays.asList(new String[] { "Flags", "pDescription", "pName", "pComment" });
            }

            public PRINTER_INFO_1() {
            }

            public PRINTER_INFO_1(int size) {
                super(new Memory(size));
            }
        }

        public static class PRINTER_INFO_2 extends Structure {
            public String pServerName;
            public String pPrinterName;
            public String pShareName;
            public String pPortName;
            public String pDriverName;
            public String pComment;
            public String pLocation;
            public INT_PTR pDevMode;
            public String pSepFile;
            public String pPrintProcessor;
            public String pDatatype;
            public String pParameters;
            public INT_PTR pSecurityDescriptor;
            public int Attributes;
            public int Priority;
            public int DefaultPriority;
            public int StartTime;
            public int UntilTime;
            public int Status;
            public int cJobs;
            public int AveragePPM;

            protected List<String> getFieldOrder() {
                return Arrays.asList(new String[] { "pServerName", "pPrinterName", "pShareName", "pPortName", 
                        "pDriverName", "pComment", "pLocation", "pDevMode", "pSepFile", "pPrintProcessor", 
                        "pDatatype", "pParameters", "pSecurityDescriptor", "Attributes", "Priority", "DefaultPriority",
                        "StartTime", "UntilTime", "Status", "cJobs", "AveragePPM" });
            }

            public PRINTER_INFO_2() {
            }

            public PRINTER_INFO_2(int size) {
                super(new Memory(size));
            }
        }

        public static class PRINTER_INFO_4 extends Structure {
            public String pPrinterName;
            public String pServerName;
            public DWORD Attributes;

            protected List<String> getFieldOrder() {
                return Arrays.asList(new String[] { "pPrinterName", "pServerName", "Attributes" });
            }

            public PRINTER_INFO_4() {
            }

            public PRINTER_INFO_4(int size) {
                super(new Memory(size));
            }
        }

        int PRINTER_ENUM_DEFAULT = 0x00000001;
        int PRINTER_ENUM_LOCAL = 0x00000002;
        int PRINTER_ENUM_CONNECTIONS = 0x00000004;
        int PRINTER_ENUM_FAVORITE = 0x00000004;
        int PRINTER_ENUM_NAME = 0x00000008;
        int PRINTER_ENUM_REMOTE = 0x00000010;
        int PRINTER_ENUM_SHARED = 0x00000020;
        int PRINTER_ENUM_NETWORK = 0x00000040;

        int PRINTER_ENUM_EXPAND = 0x00004000;
        int PRINTER_ENUM_CONTAINER = 0x00008000;

        int PRINTER_ENUM_ICONMASK = 0x00ff0000;
        int PRINTER_ENUM_ICON1 = 0x00010000;
        int PRINTER_ENUM_ICON2 = 0x00020000;
        int PRINTER_ENUM_ICON3 = 0x00040000;
        int PRINTER_ENUM_ICON4 = 0x00080000;
        int PRINTER_ENUM_ICON5 = 0x00100000;
        int PRINTER_ENUM_ICON6 = 0x00200000;
        int PRINTER_ENUM_ICON7 = 0x00400000;
        int PRINTER_ENUM_ICON8 = 0x00800000;
        int PRINTER_ENUM_HIDE = 0x01000000;
    }
}

WinspoolUtil:

import Model.WinspoolUpdate.WinspoolLib;
import Model.WinspoolUpdate.WinspoolLib.PRINTER_INFO_2;

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import com.sun.jna.platform.win32.Winspool.PRINTER_INFO_1;
import com.sun.jna.platform.win32.Winspool.PRINTER_INFO_4;
import com.sun.jna.ptr.IntByReference;

public class WinspoolUtils2 {   
        public static PRINTER_INFO_1[] getPrinterInfo1() {
            IntByReference pcbNeeded = new IntByReference();
            IntByReference pcReturned = new IntByReference();
            WinspoolLib.INSTANCE.EnumPrinters(WinspoolLib.PRINTER_ENUM_LOCAL,
                    null, 1, null, 0, pcbNeeded, pcReturned);
            if (pcbNeeded.getValue() <= 0) {
                return new PRINTER_INFO_1[0];
            }

            PRINTER_INFO_1 pPrinterEnum = new PRINTER_INFO_1(pcbNeeded.getValue());
            if (!WinspoolLib.INSTANCE.EnumPrinters(WinspoolLib.PRINTER_ENUM_LOCAL,
                    null, 1, pPrinterEnum.getPointer(), pcbNeeded.getValue(), pcbNeeded, pcReturned)) {
                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }

            pPrinterEnum.read();

            return (PRINTER_INFO_1[]) pPrinterEnum.toArray(pcReturned.getValue());
        }

        public static PRINTER_INFO_2[] getPrinterInfo2() {
            IntByReference pcbNeeded = new IntByReference();
            IntByReference pcReturned = new IntByReference();
            WinspoolLib.INSTANCE.EnumPrinters(WinspoolLib.PRINTER_ENUM_LOCAL,
                    null, 2, null, 0, pcbNeeded, pcReturned);
            if (pcbNeeded.getValue() <= 0) {
                return new PRINTER_INFO_2[0];
            }

            PRINTER_INFO_2 pPrinterEnum = new PRINTER_INFO_2(pcbNeeded.getValue());
            if (!WinspoolLib.INSTANCE.EnumPrinters(WinspoolLib.PRINTER_ENUM_LOCAL,
                    null, 2, pPrinterEnum.getPointer(), pcbNeeded.getValue(), pcbNeeded, pcReturned)) {
                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }

            pPrinterEnum.read();

            return (PRINTER_INFO_2[]) pPrinterEnum.toArray(pcReturned.getValue());
        }


        public static PRINTER_INFO_4[] getPrinterInfo4() {
            IntByReference pcbNeeded = new IntByReference();
            IntByReference pcReturned = new IntByReference();
            WinspoolLib.INSTANCE.EnumPrinters(WinspoolLib.PRINTER_ENUM_LOCAL,
                    null, 4, null, 0, pcbNeeded, pcReturned);
            if (pcbNeeded.getValue() <= 0) {
                return new PRINTER_INFO_4[0];
            }

            PRINTER_INFO_4 pPrinterEnum = new PRINTER_INFO_4(pcbNeeded.getValue());
            if (!WinspoolLib.INSTANCE.EnumPrinters(WinspoolLib.PRINTER_ENUM_LOCAL,
                    null, 4, pPrinterEnum.getPointer(), pcbNeeded.getValue(), pcbNeeded, pcReturned)) {
                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }

            pPrinterEnum.read();

            return (PRINTER_INFO_4[]) pPrinterEnum.toArray(pcReturned.getValue());
        }

        public static PRINTER_INFO_2 getPrinterInfo2(String printerName) {
            IntByReference pcbNeeded = new IntByReference();
            IntByReference pcReturned = new IntByReference();
            HANDLEByReference pHandle = new HANDLEByReference();

            WinspoolLib.INSTANCE.OpenPrinter(printerName, pHandle, null);

            WinspoolLib.INSTANCE.GetPrinter(pHandle.getValue(), 2, null, 0, pcbNeeded);
            if (pcbNeeded.getValue() <= 0) {
                return new PRINTER_INFO_2();
            }           

            PRINTER_INFO_2 pinfo2 = new PRINTER_INFO_2(pcbNeeded.getValue());

            WinspoolLib.INSTANCE.GetPrinter(pHandle.getValue(), 2, pinfo2.getPointer(), pcbNeeded.getValue(), pcReturned);          

            pinfo2.read();
            return (PRINTER_INFO_2) pinfo2;
        }

    }

Основной класс, вызывающий три реализованные функции и отображающий результат:

public static void main(String[] args) {

        for(PRINTER_INFO_1 printerInfo : WinspoolUtils2.getPrinterInfo1()) {

            System.out.println(printerInfo.pName + ": " + printerInfo.pDescription);             

        }

        for(PRINTER_INFO_2 printerInfo : WinspoolUtils2.getPrinterInfo2()) {

            System.out.println(printerInfo.pPrinterName + ": " + printerInfo.pDriverName);             

        }

        PRINTER_INFO_2 printerInfo = WinspoolUtils2.getPrinterInfo2("Canon iR-ADV C7000s GX300 V2.0");
        System.out.println(printerInfo.pPrinterName + ": " + printerInfo.pDriverName);        
    }

Я изо всех сил пытался наконец заставить его работать, поэтому я надеюсь, что это поможет.

Если вы хотите добавить некоторые другие функции API диспетчера очереди печати, Здесь вы можете найти ссылки на этот API.

Примечание. Проблема по-прежнему существует, поскольку я хотел бы, чтобы это приложение было многоплатформенным, а это решение работает только для Windows. Поэтому в будущем мне придется найти решение для получения имени драйвера принтера в ОС Linux и Mac OS X. Я буду держать вас в курсе, если что-нибудь найду.


person Padrus    schedule 18.03.2013    source источник
comment
..задавался три или четыре раза, но, кажется, никто не нашел ответа. Это может быть так, если нет ответа. Где эти повторяющиеся вопросы? Дайте ссылки.   -  person Andrew Thompson    schedule 18.03.2013
comment
Я добавил ссылку на аналогичный вопрос в stackoverflow, я знаю, что пару раз задавал этот же вопрос на других веб-сайтах в своих исследованиях, но больше не могу найти ссылки.   -  person Padrus    schedule 18.03.2013
comment
Моя организация называет свои принтеры так — HP2200-RM623. Когда я выбираю принтер, тип и местоположение отображаются в диалоговом окне «Печать». Возможно, вам придется использовать другой язык, кроме Java, для получения информации о принтере, которую вы затем можете передать в Java через JNI.   -  person Gilbert Le Blanc    schedule 18.03.2013
comment
Проблема в том, что я использую Java, чтобы сделать это приложение мультиплатформенным. Использование другого языка может убить легко переносимую часть проекта.   -  person Padrus    schedule 18.03.2013


Ответы (3)


(Выбор некоторых частей вопроса, на которые можно ответить.)

Похоже, это известная «ошибка», и я не очень понимаю оценку.

На самом деле это запрос на улучшение (RFE), а не ошибка.

Оценка просто указывает, что рецензент считает, что он/она нашел способ, они могут реализовать это в Windows... если и когда они доберутся до этого. Это не обходной путь для вас.


В этом связанном вопросе говорится о получении модели принтера с использованием SNMP.

Вы должны иметь возможность сопоставить это с решением, используя библиотеку Java SNMP. Этот подход предполагает, что принтер подключен к сети и поддерживает SNMP... но я думаю, вы уже поняли это.

person Stephen C    schedule 18.03.2013
comment
Спасибо за это объяснение, оно не отвечает на мой вопрос, но, по крайней мере, я кое-что узнал. - person Padrus; 18.03.2013
comment
На самом деле я пытаюсь это сделать, но я никогда не имел дело с SNMP, поэтому это может занять у меня много времени. - person Padrus; 18.03.2013
comment
Я пытался получить его через SNMP. Отправка команды 1.3.6.1.2.1.25.3.2.1.3.1 на принтер, похоже, сработала, но я не уверен, что она работает на всех принтерах с этим OID, поэтому в конце концов я решил получить имя драйвера с помощью JNA и Winspool.drv. Я отредактирую свой вопрос и добавлю в него свой код. - person Padrus; 20.03.2013

Для тех, кто интересуется этой темой, я продолжал искать решение, которое использовало бы базовые библиотеки Java, но, когда я начал просматривать исходный код этих библиотек, я обнаружил, что большинство методов, которые должны возвращать атрибуты принтера никогда не были реализованы, они всегда возвращают значение Null. Итак, на данный момент нет способа сделать это на Java.

Есть два обходных пути, которые я разместил во втором редактировании моего исходного вопроса:

  • Получите эту информацию через вызовы SNMP.
  • Используйте библиотеку DLL диспетчера очереди печати Windows и извлеките эту информацию из драйвера.
person Padrus    schedule 22.04.2013
comment
Работа с Java и печать только в Windows — вот все, что я пытался сделать. Большое спасибо, что поделились своей работой! - person Joe; 24.10.2016

Используя JNA, правильная версия для решения OP (без дублированного кода):

ВинспулЭкст

import java.util.Arrays;
import java.util.List;

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.Winspool;
import com.sun.jna.platform.win32.WinDef.INT_PTR;
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.win32.W32APIOptions;

public interface WinspoolExt extends Winspool {

    WinspoolExt INSTANCE = (WinspoolExt) Native.loadLibrary("Winspool.drv", WinspoolExt.class,
            W32APIOptions.UNICODE_OPTIONS);

    boolean GetPrinter(HANDLE hPrinter, int Level, Pointer pPrinter, int cbBuf, IntByReference pcbNeeded);

    boolean OpenPrinter(String pPrinterName, HANDLEByReference phPrinter, Pointer pDefault);

    public static class PRINTER_INFO_2 extends Structure {
        public String pServerName;
        public String pPrinterName;
        public String pShareName;
        public String pPortName;
        public String pDriverName;
        public String pComment;
        public String pLocation;
        public INT_PTR pDevMode;
        public String pSepFile;
        public String pPrintProcessor;
        public String pDatatype;
        public String pParameters;
        public INT_PTR pSecurityDescriptor;
        public int Attributes;
        public int Priority;
        public int DefaultPriority;
        public int StartTime;
        public int UntilTime;
        public int Status;
        public int cJobs;
        public int AveragePPM;

        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "pServerName", "pPrinterName", "pShareName", "pPortName",
                    "pDriverName", "pComment", "pLocation", "pDevMode", "pSepFile", "pPrintProcessor", "pDatatype",
                    "pParameters", "pSecurityDescriptor", "Attributes", "Priority", "DefaultPriority", "StartTime",
                    "UntilTime", "Status", "cJobs", "AveragePPM" });
        }

        public PRINTER_INFO_2() {
        }

        public PRINTER_INFO_2(int size) {
            super(new Memory(size));
        }
    }
}

WinspoolUtilExt

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinNT.HANDLEByReference;
import com.sun.jna.platform.win32.WinspoolUtil;
import com.sun.jna.ptr.IntByReference;

import WinspoolExt.PRINTER_INFO_2;

public class WinspoolUtilExt extends WinspoolUtil {

    public static PRINTER_INFO_2[] getPrinterInfo2() {
        IntByReference pcbNeeded = new IntByReference();
        IntByReference pcReturned = new IntByReference();
        WinspoolExt.INSTANCE.EnumPrinters(WinspoolExt.PRINTER_ENUM_LOCAL, null, 2, null, 0, pcbNeeded, pcReturned);
        if (pcbNeeded.getValue() <= 0) {
            return new PRINTER_INFO_2[0];
        }

        PRINTER_INFO_2 pPrinterEnum = new PRINTER_INFO_2(pcbNeeded.getValue());
        if (!WinspoolExt.INSTANCE.EnumPrinters(WinspoolExt.PRINTER_ENUM_LOCAL, null, 2, pPrinterEnum.getPointer(),
                pcbNeeded.getValue(), pcbNeeded, pcReturned)) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }

        pPrinterEnum.read();

        return (PRINTER_INFO_2[]) pPrinterEnum.toArray(pcReturned.getValue());
    }

    public static PRINTER_INFO_2 getPrinterInfo2(String printerName) {
        IntByReference pcbNeeded = new IntByReference();
        IntByReference pcReturned = new IntByReference();
        HANDLEByReference pHandle = new HANDLEByReference();

        WinspoolExt.INSTANCE.OpenPrinter(printerName, pHandle, (Pointer) null);

        WinspoolExt.INSTANCE.GetPrinter(pHandle.getValue(), 2, null, 0, pcbNeeded);
        if (pcbNeeded.getValue() <= 0) {
            return new PRINTER_INFO_2();
        }

        PRINTER_INFO_2 pinfo2 = new PRINTER_INFO_2(pcbNeeded.getValue());

        WinspoolExt.INSTANCE.GetPrinter(pHandle.getValue(), 2, pinfo2.getPointer(), pcbNeeded.getValue(), pcReturned);

        pinfo2.read();
        return (PRINTER_INFO_2) pinfo2;
    }
}

По сути, он добавляет PRINTER_INFO_2 поддержка.

person IvanRF    schedule 16.12.2015