Приложение Swing не отображается при некоторых обстоятельствах - PullRequest
3 голосов
/ 02 марта 2012

Я пишу утилиту, которая поможет с тестированием гораздо более крупной системы, и мой пользовательский интерфейс вообще не отображается, если я запускаю ее через систему. Он отлично работает, когда я запускаю его сам. Подробнее:

  1. Система, которую я тестирую, имеет много процессов, управляемых контроллером. При развертывании controller.exe порождает и завершает дочерние процессы, включая мое приложение Java. Когда приложение запускается контроллером, оно работает нормально (работает, генерирует журналы и т. Д.), Но интерфейс Swing вообще не рендерится.
  2. Я могу запустить свое приложение самостоятельно из командной строки, и пользовательский интерфейс отображается очень хорошо. Согласно Process Explorer, путь, командная строка и текущий каталог идентичны значениям, наблюдаемым при запуске приложения из контроллера.
  3. Когда приложение невидимо, при нажатии «Переместить на передний план» в Process Explorer появляется диалоговое окно с надписью. «Не найдено видимых окон для этого процесса». Нажатие на ту же кнопку в процессе ручного запуска приводит к тому, что пользовательский интерфейс Swing выходит на передний план, как и ожидалось.
  4. Я тестирую это в Windows XP. Процесс контроллера работает под СИСТЕМОЙ. Я использовал трюк "at 09:45 / interactive cmd.exe" , чтобы вызвать командную строку в качестве SYSTEM для запуска вручную. Process Explorer проверяет, что оба метода (ручной / контроллер) порождают процесс Java как SYSTEM.
  5. Единственное очевидное различие между этими двумя процессами - это родители. Системный процесс порождается как дочерний элемент controller.exe, в то время как мои ручные выполнения являются дочерними по отношению к cmd.exe.
  6. Я удаленно отлаживал в невидимом процессе, используя jdwp, и все выглядит нормально. Код пользовательского интерфейса выполняется, исключений нет, и мои списки JL даже заполняются данными, которые они отслеживают. Насколько я могу сказать, все работает; Я просто не вижу ничего из этого.

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

Спасибо!

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

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class AceProbe
{
  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new Runnable()
    {
      @Override
      public void run()
      {
        JFrame frame = new JFrame("Visible?");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(new JLabel("Test"), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
      }
    });
  }
}

Запуск этого из командной строки отображает окно, как и ожидалось. Однако, когда контроллер запускает процесс, ничего не отображается. При появлении контроллера я могу удаленно отлаживать процесс, используя -agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address=8000, и проверяю, что все потоки создаются должным образом, и никаких исключений не выдается.

Моя интуиция заключается в том, что контроллер находится в какой-то странной графической конфигурации и, поскольку родительский процесс является новым процессом Java, возможно, Swing не использует правильный GraphicsDevice? Я попытался добавить этот код:

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for(GraphicsDevice d : ge.getScreenDevices())
{
  Log.println(d);
  for (GraphicsConfiguration c : d.getConfigurations())
  {
    Log.println(c);
    Log.println(c.getBounds());
  }
}

Журнал содержит это:

Win32GraphicsDevice[screen=0]
sun.awt.Win32GraphicsConfig@13f459d[dev=Win32GraphicsDevice[screen=0],pixfmt=0]
java.awt.Rectangle[x=0,y=0,width=1920,height=1080]

, что указывает на то, что есть только одно устройство с одним конфигом, так что я немного растерялся.

Ответы [ 2 ]

5 голосов
/ 02 марта 2012

У вас есть проблема с Concurency в Swing .Swing является однопоточным, и весь вывод в графический интерфейс Swing должен выполняться на EDT.

  • в основном вы можете решить эту проблему, обернув вывод в графический интерфейс в invokeLater или invokeAndWait

  • правильным способом будет вызывать фоновую задачу(s) из SwingWorker или Runnable#ThreadRunnable, выход должен быть заключен в invokeLater или invokeAndWait)

РЕДАКТИРОВАТЬ

import java.awt.*;
import java.util.*;
import javax.swing.*;

public class SSCCE extends JFrame {

    private static final long serialVersionUID = 1L;

    public SSCCE() {
        super("SSCCE");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setContentPane(createContent());
        pack();
        centerFrameOnMonitor(0);
        setVisible(true);
    }

    private JComponent createContent() {
        JPanel ret = new JPanel();
        JButton button = new JButton("I have a tooltip");
        button.setToolTipText("I wonder where this tooltip is going to popup!");
        ret.add(button);
        return ret;
    }

    /**
     * Centers this frame in the middle of the monitor with the monitorIndex specified. This method assumes that all monitors are laid out
     * horizontally. The 0th index monitor would be the one furthest left.
     * @param monitorIndex
     */
    private void centerFrameOnMonitor(int monitorIndex) {
        GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] devices = e.getScreenDevices();
        if (monitorIndex < 0 || monitorIndex >= devices.length) {
            throw new RuntimeException("Monitor Index out of bounds: " + monitorIndex);
        }
        Arrays.sort(devices, new Comparator<GraphicsDevice>() {

            @Override
            public int compare(GraphicsDevice a, GraphicsDevice b) {
                return (int) (a.getDefaultConfiguration().getBounds().getX() - b.getDefaultConfiguration().getBounds().getX());
            }
        });
        GraphicsConfiguration gc = devices[monitorIndex].getDefaultConfiguration();
        Rectangle gcBounds = gc.getBounds();
        Point p = new Point((int) (gcBounds.getX() + (gcBounds.getWidth() / 2 - this.getWidth() / 2)), (int) (gcBounds.getY() + (gcBounds.getHeight() / 2 - this.getHeight() / 2)));
        this.setLocation(p);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                SSCCE sSCCE = new SSCCE();
            }
        });
    }
} 

EDIT2:

Возможности Повторы теста

enter image description here

от HP Elite, WinXp, Java6, низкопрофильный графический процессор

enter image description here

2 голосов
/ 06 марта 2012

Решение состоит в том, чтобы разделить приложение на 2 компонента (сбор данных и пользовательский интерфейс / обработка) и заставить их общаться через RMI .

Оказывается, что службы Windows не могут порождать GUI. Я сделал sc query и обнаружил, что controller.exe - это служба. Поскольку он является родителем процесса java, java получает те же разрешения на обслуживание, что означает отсутствие графического интерфейса пользователя. Есть некоторые обходные пути , но они существуют для обратной совместимости, и их действительно следует избегать в новом коде.

Важно повторить, что java не создавал никаких исключений. Кажется, что эта ситуация должна что-то выдать, чтобы указать, что у процесса java нет разрешения на создание новых окон, но код работает «нормально». Окно просто никогда не появляется.

Итог: если ваш vm запускается другим процессом, и вы не получаете ошибок, но нет интерфейса, убедитесь, что ваш vm не запускается как сервис. Если это так, вам нужно извлечь код пользовательского интерфейса в отдельный процесс.

...