在Java中,我如何获取打印机的制造商和型号?

10
我正在开发一个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"。
一些研究引导我来到这个页面:https://bugs.java.com/bugdatabase/view_bug?bug_id=4673400
看起来这是一个已知的"bug",但我对评估结果并不太理解。
我在考虑通过向打印机发送SNMP请求来获取制造商和型号,但我对SNMP一无所知,也不确定是否有一个单一的SNMP命令可以获取任何打印机的制造商和型号。
如果有人对如何实现这一点有任何想法,无论是使用Java方法还是发送SNMP命令或者其他任何可以在任何操作系统上完成的方法,您的帮助将不胜感激。
编辑:
这里有一个主题链接,同样提出了这个问题:
- 如何在javax.print中获取打印机的型号? 编辑2:

解决方案:

正如我在评论中所说,我尝试通过发送OID“1.3.6.1.2.1.25.3.2.1.3.1”到打印机来通过SNMP获取制造商和型号信息。这似乎有效,但我不确定它是否适用于使用相同OID的任何打印机,并且如果目标打印机上禁用了SNMP,可能会导致崩溃。

因此,我最终选择使用JNA和Winspool.drv来获取驱动程序名称。其中一部分已经在JNA中实现,但我需要添加一些结构和函数。

这是现有的JNA中WinspoolUtil.javaWinspool.java类的链接。

这是我个人更新的这两个类的代码。

Winspool:

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 上获取打印机驱动程序名称的解决方案。如果我找到了什么,我会及时告诉你。

2
这个问题已经被问了三到四次,但似乎没有人找到答案。如果没有答案的话,可能是这样的情况。这些重复的问题在哪里?请提供链接。 - Andrew Thompson
我在 stackoverflow 上添加了一个类似问题的链接,我知道在我的研究中我曾经遇到过几次这个相同的问题,但是现在找不到链接了。 - Padrus
我的组织将打印机命名为类似于HP2200-RM623的名称。当我选择打印机时,类型和位置会显示在打印对话框中。您可能需要使用除Java之外的其他语言来获取打印机信息,然后通过JNI传递给Java。 - Gilbert Le Blanc
问题是我正在使用Java使这个应用程序具备多平台性。使用另一种语言可能会破坏项目的“易于移植”的特点。 - Padrus
3个回答

3
< p>(挑选一些可回答的问题部分。)

这似乎是一个已知的“错误”,我真的不理解这个评估。

实际上,这是一个“请求增强”(RFE),而不是一个错误。

评估只是表示评论者认为他/她已经找到了一种方法,可以在Windows上实现这个功能……如果他们有时间的话。 这不是你的解决方法。


这个相关的问题谈到了使用SNMP获取打印机型号

你应该能够用Java SNMP库将其映射到一种解决方案。 这种方法假设打印机已经网络化,并且具备SNMP功能……但我猜你已经发现了这一点。


谢谢您的解释,虽然它没有回答我的问题,但至少我学到了一些东西。 - Padrus
我正在尝试着去处理它,但我以前从未接触过SNMP,所以可能需要我很多时间。 - Padrus
我尝试通过SNMP获取它。 向打印机发送命令“1.3.6.1.2.1.25.3.2.1.3.1”似乎可以工作,但我不确定所有具有此OID的打印机都可以使用,因此最终我选择使用JNA和Winspool.drv获取驱动程序名称。我将编辑我的问题并将我的代码添加到其中。 - Padrus

1
对于对此主题感兴趣的人,我一直在寻找一种解决方案,该方案将使用基本的Java库,但当我开始查看这些库的源代码时,我发现大多数应该返回打印机属性的方法从未被实现,它们总是返回Null值。因此,目前没有办法在Java中完成这项任务。
有两种解决方法,我在原问题的第二次编辑中发布了它们:
  • 通过SNMP调用获取这些信息。
  • 使用Windows打印池DLL并从驱动程序中检索这些信息。

我只是想在Windows上使用Java并进行打印。非常感谢您分享您的工作! - Joe

0
使用JNA,没有重复代码的OP解决方案的正确版本是这个: WinspoolExt
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支持。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接