Обновлять принтеры в Java во время работы приложения

Как видно из заголовка, я бы хотел обновить принтеры, зарегистрированные в настройках компьютера, когда запущено мое Java-приложение. Обычно я могу использовать PrinterJob.lookupPrintServices() чтобы получить принтеры. Однако они обновляются только при перезапуске приложения. Я прочитал кое-что о том, что lookupPrintServices() должно быть сделано в новом потоке, чтобы получить принтеры. Это, однако, не сработало, список принтеров остался прежним. Следующая ссылка показывает, что эта проблема должна быть исправлена ​​в Java 5.0, я что-то не так делаю?

Любая помощь высоко ценится!

РЕДАКТИРОВАТЬ Добавлено MWE.

public class MTPrinterTest extends Thread {
    public static void main(String[] args) {
        MTPrinterTest t1 = new MTPrinterTest();
        t1.start();

        try {
            System.in.read();
        } catch (Exception e){}

        MTPrinterTest t2 = new MTPrinterTest();
        t2.start();
    }
    public void run() {
        PrinterJob printerJob;
        PrintService[] printServices;

        printerJob = PrinterJob.getPrinterJob();
        printServices = printerJob.lookupPrintServices();
        System.out.println("Number of servies found: " + printServices.length);
        for (int i =0; i< printServices.length; i++)
            System.out.println("--> Available Printer " + i + ": " + printServices[i]);
        printerJob.printDialog(); 
    } 
}

5 ответов

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

Здесь я нашел решение:

/**
 * Printer list does not necessarily refresh if you change the list of 
 * printers within the O/S; you can run this to refresh if necessary.
 */
public static void refreshSystemPrinterList() {
    Class<?>[] classes = PrintServiceLookup.class.getDeclaredClasses();
    for (int i = 0; i < classes.length; i++) {
        if ("javax.print.PrintServiceLookup$Services".equals(classes[i].getName())) {
            sun.awt.AppContext.getAppContext().remove(classes[i]);
            break;
        }
    }
}

В основном, статический класс PrintServiceLookup.Services ведет список услуг печати. Итак, если вы удалите этот класс из AppContext, ты заставляешь PrintServiceLookup создать новый экземпляр снова. Таким образом, список услуг печати обновляется.

Я сталкивался с одной и той же проблемой до и после нескольких тестов, кажется, что список принтеров снимается в начале приложения Java и не может быть обновлен после этого с помощью Java lookupPrintServices(),

Что я сделал, чтобы решить эту проблему, так это вызвать API Winspool напрямую с помощью JNA. Если вы собираетесь это сделать, API Winspool хорошо документировано Microsoft: документация по Winspool API.

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

На основании этого решения связи , предложенной tresf проблемной ссылки и в предыдущем ответе IvanRF, я создал новый исходный код , который является слиянием обоего кодов.

Целью этого кода помимо решения проблемы является устранение предупреждения:

AppContext - это внутренний проприетарный API, который может быть удален в будущем выпуске.

      public static boolean refreshSystemPrinters() {
    try {
        for (final Thread thread : Thread.getAllStackTraces().keySet()) {
            if (thread.getName().equals("PrinterListener")) {
                thread.interrupt();
            }
        }
        final Class<?> appContextClass = Class.forName("sun.awt.AppContext");
        final Object appContext = appContextClass.getMethod("getAppContext").invoke(null);
        appContextClass.getMethod("put", Object.class, Object.class).invoke(appContext, javax.print.PrintServiceLookup.class.getDeclaredClasses()[0], null);
        javax.print.PrintServiceLookup.lookupPrintServices(null, null);
        return true;
    } catch (final Exception e) {
        e.printStackTrace();
    }
    return false;
}

Этот код с использованием отражения Java:

      final Class<?> appContextClass = Class.forName("sun.awt.AppContext");
final Object appContext = appContextClass.getMethod("getAppContext").invoke(null);
appContextClass.getMethod("put", Object.class, Object.class).invoke(appContext, javax.print.PrintServiceLookup.class.getDeclaredClasses()[0], null);

эквивалентно этому:

      sun.awt.AppContext.getAppContext().put(javax.print.PrintServiceLookup.class.getDeclaredClasses()[0], null);

Быстрый обходной путь для систем на основе CUPS:

System.setProperty("sun.java2d.print.polling", "false");

Предупреждение, это имеет побочный эффект постепенного замедления вызовов (например, 80ms) чтобы PrintServiceLookup.lookupPrintServices(...),

В частности:

Свойство sun.java2d.print.polling установлен в true:

  • <1msзвонитьPrintServiceLookup.lookupPrintServices(...)

Свойствоsun.java2d.print.pollingустановлен вfalse:

  • 80msзвонитьPrintServiceLookup.lookupPrintServices(...)

Хотя 80 мс - это очень короткий промежуток времени для большинства пользовательских интерфейсов, в сценарии с высокой нагрузкой это может повлиять на производительность.

Кроме того, это80msпостепенно увеличивается со временем. Например, 100000 звонковPrintServiceLookup.lookupPrintServices(...)постепенно увеличивает задержку от80msв1,000ms, Длительные программы могут испытывать заметную задержку.

Тем не менее, эта задержка по-прежнему предпочтительнее, чем AppContext Обходной путь, особенно в системах CUPS (Linux, Mac). AppContextв других решениях возникают проблемы с JVM-потоками, которые в конечном итоге приводят к2,000ms задержки, а иногда и до 200,000ms задержки (ссылка: https://github.com/qzind/tray/issues/479)

Подробный отчет об ошибке: https://github.com/AdoptOpenJDK/openjdk-build/issues/1212

Я не уверен, что вы ожидали. Вот вывод для моего ноутбука с Windows Vista под управлением Java 7.

Number of servies found: 3
--> Available Printer 0: Win32 Printer : PamFax
--> Available Printer 1: Win32 Printer : Microsoft XPS Document Writer
--> Available Printer 2: Win32 Printer : CutePDF Writer
x
Number of servies found: 3
--> Available Printer 0: Win32 Printer : PamFax
--> Available Printer 1: Win32 Printer : Microsoft XPS Document Writer
--> Available Printer 2: Win32 Printer : CutePDF Writer

Услуги принтера не меняются каждые 5 минут, даже в общей сети.

Обновлено на основе комментария:

Ваше Java-приложение должно быть остановлено и запущено, чтобы получить обновленный список принтеров.

Я думаю, я не понимаю вашу среду. В местах, где я работал, все принтеры были определены в сети задолго до того, как мы написали какой-либо код. Изменения принтера были сделаны редко (я имею в виду один раз в год).

Другие вопросы по тегам