Accepting another child window of the process

I am writing a kind of web applet emulator. I read the webpage, found the applet options, downloaded the applet and ran it. It is very important that the applet runs its own process (i.e. not an emulator process). It should, however, display in the emulator process window.

How does a Java plugin do it? When the flag is set separate_jvm

, the plugin loads the applet in a separate JVM process, but the applet is still displayed in the same browser panel.

I made some progress by creating a loader class that on another JVM adds the target applet to an unset invisible frame and sends the frame window handle to the emulator JVM. The latter links it to the instance Canvas

with user32.SetParent

via JNA and the display works fine.

However, only mouse events are dispatched: keyboard input is not redirected. The applet reports Component#isFocusOwner

as false rather requestFocusInWindow

than making it the focus owner by returning false. How can I pass keyboard focus to the applet window handle? My current approach involves a server (emulator) that receives window handles from a client (applet). Only mouse events appear as the applet cannot receive focus.

The server class handles displaying the applet.

import com.sun.jna.*;
import com.sun.jna.platform.win32.User32;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import static com.sun.jna.platform.win32.User32.*;

public class Loader {
    private static final String APPLET_DIRECTORY = ""; // TODO: Set this to the directory containing the compiled applet

    private static ServerSocket serverSocket;
    private static JFrame frame;
    private static Canvas nativeDisplayCanvas;

    public static void main(String[] argv) throws Exception {
        nativeDisplayCanvas = new Canvas();
        frame = new JFrame("Frame redirect");
        frame.setLayout(new BorderLayout());
        frame.add(nativeDisplayCanvas, BorderLayout.CENTER);
        frame.setSize(300, 200);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        (new Thread() {
            public void run() {
                try {
                    serve();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        spawnAltJVM(APPLET_DIRECTORY, "AppletDemo");
    }

    public static void serve() throws Exception {
        serverSocket = new ServerSocket(6067);
        serverSocket.setSoTimeout(10000);

        while (true) {
            try {
                System.out.println("Waiting for applet on port " + serverSocket.getLocalPort() + "...");
                Socket server = serverSocket.accept();
                System.out.println("Connected to " + server.getRemoteSocketAddress());
                BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
                DataOutputStream out = new DataOutputStream(server.getOutputStream());
                while (true) {
                    String msg = in.readLine();
                    if (msg != null && msg.startsWith("child_hwnd")) {
                        windowCreatedHandler(msg);
                        out.writeUTF("hwnd_recv\n");
                        out.flush();
                    }
                }
            } catch (IOException ex) {
                System.out.println("Something happened to the socket...");
                break;
            }
        }
    }

    public static void windowCreatedHandler(String message) {
        String[] tokens = message.split(":");
        final User32 user32 = User32.INSTANCE;
        HWND child_applet = new HWND(Pointer.createConstant(Long.parseLong(tokens[1])));
        final HWND child_frame = new HWND(Pointer.createConstant(Long.parseLong(tokens[2])));

        frame.addComponentListener(
                new ComponentAdapter() {
                    @Override
                    public void componentResized(ComponentEvent e) {
                        user32.SetWindowPos(child_frame, new HWND(Pointer.NULL), 0, 0, frame.getWidth(), frame.getHeight(), 0);
                    }
                }
        );
        HWND parent = new HWND(Native.getComponentPointer(nativeDisplayCanvas));

        user32.SetParent(child_applet, parent);

        int style = user32.GetWindowLong(child_frame, GWL_STYLE) & ~WS_POPUP | (WS_CHILD | WS_VISIBLE);
        user32.SetWindowLong(child_applet, GWL_STYLE, style);
        user32.SetWindowPos(child_applet, new HWND(Pointer.NULL), 0, 0, nativeDisplayCanvas.getWidth(), nativeDisplayCanvas.getHeight(), 0);
    }

    public static void spawnAltJVM(String cp, String clazz) throws IOException, InterruptedException, ClassNotFoundException {
        ProcessBuilder processBuilder = new ProcessBuilder(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java", "-cp", cp, clazz);
        Process applet = processBuilder.start();
        final BufferedReader in = new BufferedReader(new InputStreamReader(applet.getInputStream()));
        final BufferedReader err = new BufferedReader(new InputStreamReader(applet.getErrorStream()));
        (new Thread() {
            public void run() {
                while (true) {
                    try {
                        System.out.println("[client] " + in.readLine());
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

      

Meanwhile, the client class just instantiates and processes messages.

import sun.awt.windows.WComponentPeer;
import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingDeque;

public class AppletDemo extends Applet {
    private Canvas canvas;
    private static Color backgroundColor = Color.RED;

    public AppletDemo() {
        setLayout(new BorderLayout());
        canvas = new Canvas();
        add(canvas, BorderLayout.CENTER);
        setBackground(Color.CYAN);
        canvas.addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                refreshColors();
            }
        });
        canvas.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                refreshColors();
            }
        });
    }

    private void refreshColors() {
        SwingUtilities.invokeLater(
                new Runnable() {
                    @Override
                    public void run() {
                        backgroundColor = (backgroundColor == Color.RED ? Color.GREEN : Color.RED);
                        canvas.setBackground(backgroundColor);
                    }
                }
        );
    }

    public static void main(String[] argv) throws Exception {
        System.setErr(System.out);

        final AppletDemo app = new AppletDemo();
        Frame frame = new Frame("AppletViewer");
        frame.setLayout(new BorderLayout());
        frame.add(app, BorderLayout.CENTER);
        frame.setUndecorated(true);
        frame.pack(); // Create the native peers
        frame.setSize(300, 200);

        final Socket client = new Socket("localhost", 6067);
        final LinkedBlockingDeque<String> messageQueue = new LinkedBlockingDeque<>();
        final DataOutputStream out = new DataOutputStream(client.getOutputStream());
        final BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        (new Thread() {
            public void run() {
                while (true) {
                    try {
                        out.writeBytes(messageQueue.take() + "\n");
                        out.flush();
                    } catch (IOException | InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }).start();
        (new Thread() {
            public void run() {
                while (true) {
                    try {
                        if ("hwnd_recv".equals(in.readLine())) {
                            // Attempt to grab focus in the other process' frame
                            System.out.println("Trying to request focus...");
                            System.out.println(app.requestFocusInWindow());
                        }
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }).start();

        messageQueue.add("child_hwnd:" + ((WComponentPeer) app.getPeer()).getHWnd() + ":" + ((WComponentPeer) frame.getPeer()).getHWnd());
    }
}

      

They are a bit long because they need some socket work, but they compile and should exhibit a problem. They require JNA compilation. I cut them as much as possible with some good practice.

When executed Loader

, a window should appear redirecting the canvas AppletDemo

. Mouse events are computed: canvas toggles between red and green per mouse. Ideally, the same behavior should occur for keystrokes.

I used WinSpy to get the notepad.exe window and textbar handles and hardcoding the handles into Loader

. Keyboard focus works fine with a multi-line edit control, but not with the top window itself.
Why? Is it related to what I have?

I opened a Chrome window with an applet in WinSpy and found that the plugin doesn't create a dummy Frame

- the applet canvas is directly installed as a child of Chrome. However, I was unable to create my own peer for Applet

as it seems to require it to be displayed.

I've read about the dangers of cross-process parent / child or owner / owner of a window relationship , but I can't think of a better way to translate the child applet into the emulator.

+3


source to share


2 answers


Since you really want to create the applet as a child window, a simple solution would be to convince the applet to be your children, rather than emphatically use it and work with both Windows and the JVM.

Fortunately, the Sun / Oracle Java Virtual Machine comes with a class called WComponentFrame

(Windows only, as the name implies). It can be created from hwnd

, which you can submit from your parent process. The applet can then be added as a child of your window.



import sun.awt.windows.WComponentPeer;

frame = new WEmbeddedFrame(hwnd);
frame.setLayout(new BorderLayout());
frame.add(applet, BorderLayout.CENTER);

      

+1


source


It looks like you are trying to pass an event to a Canvas object that you are not explicitly setting Focusable (true) for.

If so, then in your AppletDemo constructor try:

canvas.setFocusable(true);
canvas.requestFocus();

      

It also seems like you want to pass key events to your applet and not your canvas, from your question.



In that case, try this in your AppletDemo constructor:

this.setFocusable(true);
this.requestFocus();

      

After that, you should receive the default keyboard input for the lumped component.

0


source







All Articles