如何在Java中检查PC是否连接到网络打印机?

9
基本上,我需要检查网络打印机的状态,是否打开。有没有办法用Java实现这个功能?是否有第三方API或工具可以使用?
我尝试了在Java中使用PrintServiceLookup,但它并不能提供打印机是否打开的状态。
另外,如果在Java中不可能实现,是否有任何在Windows中运行的命令可以给出打印机的状态?
然后,我可以在Java中运行此命令并检查状态。

你尝试过检查PrintService上的PrinterIsAcceptingJobs属性吗? - clstrfsck
是的,它总是显示打印机正在接受作业:接受作业,即使打印机未连接到计算机。 - ajm
打印机是否具有网络API?您可以通过http协议连接并执行所需的查询吗?这只是一个猜测,我以前没有做过这个。 - Anatoly
您事先知道网络打印机的IP地址吗? - codefreaK
我有打印机的IP地址。但它也可能是一个USB打印机。 - ajm
请查看以下帖子中提供的答案。 - ag112
6个回答

4
根据 "网络打印工作原理",这实际上取决于打印机的类型和支持的协议。如果您知道打印机使用的IP和端口,并且如果您的打印机支持SNMP(只是为了选择协议),则可以使用SNMP协议查询打印机信息。有一个Java库SNMP4j可帮助您实现此目的。我建议除非对于您的设置,打印机IP端口永远不会(!)更改,否则不要使用它。因为您可能会遇到几个问题:
  • 如何发现未知的打印机?
  • 如何发现打印机所使用的端口?
  • 如何发现打印机所使用的协议?
假设上述问题不是什么大问题,而且假设每台打印机都支持SNMP。那么如何获取其中的信息呢?除了使用上述的Java库外,你还可以在Linux终端中使用snmpget。语法如下:
snmpget -v1 -c public host-ip OID

OID是打印机每个属性的对象标识符,范围从pagecounttoner-cardridge信息。如果您不添加OID,则会获取可用OID的整个列表。问题的关键是,尽管所有OID都是标准化的,但OID的使用因品牌和型号而异。对于我的HP,以下内容有效:
snmpget -v1 -c public 192.168.1.10 iso.3.6.1.2.1.43.17.6.1.5.1.2

并返回

iso.3.6.1.2.1.43.17.6.1.5.1.2 = STRING: "Ready"

使用OID可以返回我的惠普打印机状态。但是如果我在佳能打印机上使用相同的OID,我会得到

Error in packet
Reason: (noSuchName) There is no such variable name in this MIB.
Failed object: iso.3.6.1.2.1.43.17.6.1.5.1.2

因此,即使不考虑其他可用的协议,SNMP也不适用于所有情况。
考虑到所有这些信息,在我看来最简单的方法就是通过此代码检查是否可以在常见的打印机端口之一建立与打印机的连接。
boolean available = false;
try {
    String serverAddress = "192.168.1.10";
    Socket s = new Socket(serverAddress, 9100);
    s.close();
    available = true;
} catch (IOException e) {
    available = false;
}
System.out.println("printer available: " + available);

当然,这仅在您已经知道打印机的 IP 地址时才有效。


这个字符串 "iso.3.6.1.2.1.43.17.6.1.5.1.2" 是什么? - ajm
这是OID(http://www.net-snmp.org/wiki/index.php/OID),您打印机的每个属性(打印的页面数,墨盒状态等)都有自己的OID。因此,如果您想查询打印机的某些内容,则首先需要获取要查询的属性的OID。但不幸的是,每台打印机分配的OID都不同。 - Westranger

2
如果您知道打印机的IP地址,您可以使用此代码对其进行ping操作,以检查它是否接受作业。
import java.io.IOException;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.attribute.Attribute;
import javax.print.attribute.AttributeSet;

public class PrinterStatus {

    public static void main(String[] args) {

    PrintService printer = PrintServiceLookup.lookupDefaultPrintService();
    AttributeSet att = printer.getAttributes();

    String ip = "0.0.0.0"; // IP Address of your printer
    boolean check = false;

    for (Attribute a : att.toArray()) {
        if (att.get(a.getClass()).toString().equalsIgnoreCase("accepting-jobs")){
            check = true;
        }
    }

    if (check && runSystemCommand(ip)) System.out.println("Printer ready!");
}

public static boolean runSystemCommand(String ip) {

    ProcessBuilder pb = new ProcessBuilder("ping", "-c 1", ip);
    Process p;
    try {
        p = pb.start();
        return p.waitFor() == 0 ? true : false;
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return false;
  }
}

当然,在您的情况下,您需要稍微更改此代码才能使用它。除了本地访问之外,我没有看到更好的解决方案。

我以前尝试过这个,但是即使打印机已经关闭,accepting-jobs属性始终返回true。 - ajm
是的,但是如果你拥有IP地址,它却被关闭了,你将无法ping通它。 - m.aibin
但是你能够ping通设备吗?如果可以,那就有点奇怪了。 - m.aibin
不行,当设备断开连接时,我无法ping通它。 - ajm
现在你应该知道,如果你能够ping通设备,那么很可能你可以尝试打印。 - m.aibin
显示剩余3条评论

1

有Java API可以实现本地打印功能!!请查看这个类java.awt.Desktop
java2s.com e.g


1

1
我无法想到在Java中实现这个的简单方法(虽然毫无疑问有人会想出一行代码!)。我更熟悉C#,回答您的Windows问题,我将使用微软的WMI,具体来说是Win32_Printerhttps://msdn.microsoft.com/en-us/library/aa394363(v=vs.85).aspx)。一个旧的CodeProject网站展示了如何使用Win32_Printer在C#中测试脱机打印机(http://www.codeproject.com/Articles/6069/How-to-Check-if-your-Printer-is-Connected-using-C)。

这个人(http://henryranch.net/software/jwmi-query-windows-wmi-from-java/)开发了jWMI,可以在Java中查询WMI系统,因此这可能适用于您的情况。我不确定它有多全面,但您可以想象它可以访问Win32_Printer。它使用VBScript,在Java中通过cmd.exe执行并从stdout获取值,所以我不知道速度如何,但他还谈到了使用WMIC.exe,这可能更适合您。

从他的介绍中看来,您的代码可能非常简单:

String status = getWMIValue("Select [printer name] from Win32_Printer", "PrinterStatus");

其中7 (0x7)为离线状态。

或者

String status = getWMIValue("Select [printer name] from Win32_Printer", "Availability");

在其中可以识别各种状态(例如0x7 = 关机,或0x8 = 离线等)。

查询还允许使用“Select * from”语法,因此如果没有名称,您可以循环遍历打印机。

Win32_Printer具有一个属性(Network作为布尔值),允许您检查打印机是本地还是网络,并且这是一个旧但有趣的VBScript测试网络打印机的阅读材料(http://blogs.technet.com/b/heyscriptingguy/archive/2006/08/14/how-can-i-list-all-the-printers-on-a-remote-computer.aspx)。

这些解决方案已经过时了(我相信MI是WMI的最新版本),但如果jWMI库适合您,它可能是一个答案。


我尝试使用这个。但是即使PC与网络断开连接,我始终得到状态= 3。当我停止打印机池服务或从PC中删除打印机设备时,我就不会得到任何回应。我得到一个空白响应。 - ajm
我在某个地方读到,如果你正在检索PrinterStatus = 3或PrinterState = 0,则打印机驱动程序可能不会将准确信息提供给WMI。 WMI从spoolsv.exe进程检索打印机的信息。 打印机驱动程序可能没有向打印池程序报告其状态。 在这种情况下,Win32_Printer将报告打印机处于空闲状态。 - ajm
哦,那么有些驱动程序绕过了打印池?我不知道在这种情况下如何获取它们的状态。我不确定Win32_Printer的“Availability”属性或“ExtendedPrinterStatus”属性是否更好,但值得一试。如果这样还是不行,恐怕我已经到了我的知识尽头了。 - Ambie
ExtendedPrinterStatus 给了我 2 (0x2) 未知状态,而可用性则没有返回任何信息。 - ajm
java jWMI "Select * from Win32_Printer where name = 'WorkCentre 5325 PS'" "DriverName"。这就是我正在尝试的,它确实可以正确返回驱动程序名称为“Xerox WorkCentre 5325 PS'”。那么,在这种情况下,驱动程序是否绕过了打印机池呢? - ajm
哦,那真的很令人沮丧——所以有些属性是可靠的,而有些则不是?我敢打赌,如果你将打印机设置为打印,PrinterStatus 也会显示出来。 - Ambie

-1

我已经使用了

String WorkOffline = getWMIValue("Select * 
from Win32_Printer 
where Name='printer_name'", "WorkOffline");
returns True/False

这篇文章假设原帖作者使用的是Windows操作系统,但并不对操作系统做出任何假设。 - P.J.Meisch

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