Eric Grunzke
Eric Grunzke

Reputation: 1657

Swing app doesn't display in some circumstances

I'm writing a utility to help with testing a much larger system, and my UI doesn't show up at all if I run it through the system. It works fine when I run it myself. More details:

  1. The system I'm testing has many processes, managed by a controller. When deployed, controller.exe spawns and terminates child processes, including my java app. When launched by the controller, my app runs fine (does work, generates logs, etc.) but the Swing UI doesn't render at all.
  2. I can launch my app myself from the command line, and the UI shows up just fine. According to Process Explorer, the Path, Command Line, and Current Directory are all identical to the values observed when the app is launched from the controller.
  3. When the app is invisble, clicking "Bring to Front" in Process Explorer pops a dialog that says. "No visible windows found for this process." Clicking the same button on the manually spawned process brings the Swing UI to front as expected.
  4. I'm testing this in Windows XP. The controller process runs under SYSTEM. I used the "at 09:45 /interactive cmd.exe" trick to spawn a command prompt as SYSTEM for my manual launch. Process Explorer verifies that both methods (manual / controller) spawn the java process as SYSTEM.
  5. The only obvious difference between the two processes are the parents. The system process spawns as a child of controller.exe, while my manual executions are a child of cmd.exe.
  6. I have remotely debugged into the invisible process using using jdwp, and everything looks normal. The UI code executes, no exceptions are thrown, and my JLists are even getting populated with the data they monitor. As far as I can tell, everything works; I just can't see any of it.

I apologize if this has been answered elsewhere. I did my best to search around, but all the questions I could find are about certain components failing to render, never a failure of the entire app to display.

Thanks!

Edits: Based on feedback, I've swapped in a demo program that's as small as possible. Here's the entire code:

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);
      }
    });
  }
}

Running this from the command line displays the window as expected. However, when the controller spawns the process, nothing displays. When spawned by the controller, I can debug into the process remotely using -agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address=8000 and verify that all of the threads are spawning as expected, and that no exceptions are thrown.

My intuition from here is that controller is in some odd graphics configuration and, since is parents the new java process, maybe Swing is not using the correct GraphicsDevice? I tried adding this code:

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

The log contains this:

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

which seems to indicate there's only one device with one config, so I'm at a bit of a loss.

Upvotes: 3

Views: 821

Answers (2)

Eric Grunzke
Eric Grunzke

Reputation: 1657

The solution is to split the app into 2 components (data gathering and ui/processing) and have them communicate via RMI.

It turns out that Windows services cannot spawn GUIs. I did some sc querying and discovered that controller.exe is a service. Since it parents the java process, java gets the same service permissions, which means no gui. There are some workarounds, but they exist for backwards compatibility and really should be avoided in new code.

It is important to reiterate that java didn't raise any exceptions. It seems like this situation should throw something to indicate that the java process doesn't have permission to create new windows, but the code runs "normally." The window just never appears.

Bottom line: if another process is launching your vm, and you get no errors but no ui, check to make sure your vm isn't launching as a service. If it is, you'll need to extract ui code into a separate process.

Upvotes: 2

mKorbel
mKorbel

Reputation: 109823

You have an issue with Concurency in Swing. Swing is single threaded, and all output to the Swing GUI must be done on EDT.

  • basically you can solve that by wrapping output to the GUI in invokeLater or invokeAndWait

  • correct way would be invoke background task(s) from SwingWorker or Runnable#Thread (in Runnable, output must be wrapped into invokeLater or invokeAndWait)

EDIT

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:

Capabilities Test retuns

enter image description here

from HP Elite, WinXp, Java6, low profile GPU

enter image description here

Upvotes: 5

Related Questions