6

I need to integrate a custom spell-checker into an existing Java application without an Automation API.

It should work like this:

  1. In the external application A, the user opens a window, where he/she enters some text. In that window there is a button "Spellchecker".
  2. When the user presses the "Spellchecker" button, my program B should read the text from A's text field and put it into the custom spellchecker.

How can I detect that some button has been pressed in an external Java application?

Update 1: I tried to install my own AWT event listener to detect events in other applications.

    Toolkit.getDefaultToolkit().addAWTEventListener(new MyAWTEventListener(), AWTEvent.MOUSE_MOTION_EVENT_MASK);

    while (true)
    {
        Thread.sleep(1);
    }

But it doesn't work.

Update 2: Replacing the system event queue doesn't work, either.

private void queuePushingExperiment() throws InterruptedException,
        InvocationTargetException {
    EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();

    queue.push(new MyEventQueue());

    EventQueue.invokeAndWait(new Runnable() {

        @Override
        public void run() {
            System.out.println("run");
        }
    });
}

public class MyEventQueue extends EventQueue {

    @Override
    public SecondaryLoop createSecondaryLoop() {
        System.out.println("createSecondaryLoop");
        return super.createSecondaryLoop();
    }

    @Override
    protected void dispatchEvent(AWTEvent event) {
        System.out.println("dispatchEvent");
        super.dispatchEvent(event);
    }

    @Override
    public AWTEvent getNextEvent() throws InterruptedException {
        System.out.println("getNextEvent");
        return super.getNextEvent();
    }

    @Override
    public AWTEvent peekEvent() {
        System.out.println("peekEvent");
        return super.peekEvent();
    }

    @Override
    public AWTEvent peekEvent(int id) {
        System.out.println("peekEvent");
        return super.peekEvent(id);
    }

    @Override
    protected void pop() throws EmptyStackException {
        System.out.println("pop");
        super.pop();
    }

    @Override
    public void postEvent(AWTEvent theEvent) {
        System.out.println("postEvent");
        super.postEvent(theEvent);
    }

    @Override
    public void push(EventQueue newEventQueue) {
        System.out.println("push");
        super.push(newEventQueue);
    }

}

Update 3: java.awt.Window.getOwnerlessWindows() and EventQueueMonitor.getTopLevelWindows() both return empty arrays even though there is a JFrame open at time of their invokation.

Update 4: I noticed that I can't write to the file C:\Program Files\Java\jdk1.7.0_25\jre\lib\accessibility.properties and currently the line assistive_technologies=com.sun.java.accessibility.AccessBridge is commented out. That may cause the aforementioned problems with the accessibility objects.

Glory to Russia
  • 17,289
  • 56
  • 182
  • 325
  • 1
    Can you change the code of A? – LuigiEdlCarno Jun 20 '13 at 07:43
  • 2
    No. If I could, the task would have been very easy. – Glory to Russia Jun 20 '13 at 07:47
  • That's true. I think all you can do is listen for OS specific messages. For example Windows messages on WIN OSs. That's a really cruel solution, and I am not sure, if java allows this. – LuigiEdlCarno Jun 20 '13 at 07:51
  • You cannot change the code of A, yet the spell checker is launched by it... They did have to modify A to do that, right? Can't you tell them to copy the text into a file which would be passed as an argument to the spell checker? – fge Jun 20 '13 at 07:55
  • 1
    Could you run bth A and B app on the same jvm instance? – StanislavL Jun 20 '13 at 07:55
  • @fge A has a built-in spellchecker (the spellchecker is located in the same code base as the entire code of A), but it doesn't work good enough for my language (Russian). – Glory to Russia Jun 20 '13 at 08:03
  • @StanislavL How can I achieve this? I thought that for every Java program a separate JVM is launched. – Glory to Russia Jun 20 '13 at 08:03
  • which `os` do you use? – Dmitry Zagorulkin Jun 20 '13 at 08:04
  • @ZagorulkinDmitry Windows – Glory to Russia Jun 20 '13 at 08:05
  • Yes if they are run separately it will be. Basically I don't see anyway you can do it in Java. May be in C++ with all the memory manipulations etc. Only way to do so is to ask the people who has original source code to provide an listener registration mechanism. (Observer) which I'm sure you already know... TO sum up, it sucks to be you... – Thihara Jun 20 '13 at 08:06
  • @LuigiEdlCarno Regarding listening to OS specific messages: I'm not sure that you can control Java applications via WinAPI calls. – Glory to Russia Jun 20 '13 at 08:06
  • @DmitriPisarenko I think you can do this using JNA. I haven't used them myself yet, but maybe you find something starting [here](http://stackoverflow.com/questions/2389156/calling-win32-api-method-from-java). Since the JVM itself converts java calls to OS specific calls, it should produce WINAPI calls, when you exucte A. – LuigiEdlCarno Jun 20 '13 at 08:18
  • A wild shot here, can you change the startup command? If so maybe it would it be possible to do some load time weaving with AspectJ. If it is then you can hook into the action, if you had the source and knew where to hook in. – Shawn Vader Jun 20 '13 at 09:02
  • @ShawnVader Rather not. It's not clear whether I can do that (whether the customer will accept it). – Glory to Russia Jun 20 '13 at 10:48
  • I heard that it is possible to control running Java apps using Java Accessibility. I tried this (see update 1), but it doesn't work. What am I doing wrong? – Glory to Russia Jun 20 '13 at 10:50

1 Answers1

4

How can I achieve this? I thought that for every Java program a separate JVM is launched.

Actually java app A could be run from app B. You just call the A's main() method. So in fact you start B.main() which runs necessary B code and then calls A.main() to run A. In this case you can get started window (or frame) using Window class method.

public static Window[] getWindows()

After that just go down all the subcomponents of the found Window checking their classes and when you find JButton check the buton's text or image to find necessary instance. Then just add your listener there.

StanislavL
  • 56,971
  • 9
  • 68
  • 98