Kategorie szkoleń | Egzaminy | Kontakt
  • 3
  • 1
  • 100

Jak zrealizować zadanie?

- z aplikacji JAVA uruchomić program winowsowy, np: notepad.exe,

- sprawdzić, czy okno notatnika jest aktywne i "na wierzchu",

- wysłać sekwencję  znaków do okna notatnika, np: To jest test \n ALP+P  a  nazwa_pliku.txt  ALT+Z.

W wyniku powstaje plik o nazwie "nazwa_pliku.txt" w domyślnej lokalizacji dokumentów Windows'a z tekstem: "To jest test" i z kursorem w nowym wierszu.

Michał_Kocot
  • Zapytał
  • @ Michał_Kocot | 05.01.2016
    • 1
    • 0
    • 1

Odpowiedzi (3)

  • 0

Rozwiązanie nie jest idealne, gdyż z poziomu Javy mamy ograniczoną kontrolę nad innymi procesami...

Nie ma problemu z utworzeniem procesu, w którym zostanie uruchomiony np. notatnik (można wykorzystać metodę exec z klasy java.lang.Runtime).

Da się również wygenerować zdarzenia pochodzące od klawiatury (można użyć klasy java.awt.Robot)

Nie mam natomiast pomysłu jak sprawdzić czy inna aplikacja ma aktualnie fokus.

Podsumowując aplikacja mogłaby wyglądać następująco:

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.io.IOException;

public class NotepadRunner {
  private final static int DELAY = 1000;

  private static void sendText(Robot robot, String text) {
    for (int i = 0; i < text.length(); i++) {
      char c = text.charAt(i);
      boolean upperCase = Character.isUpperCase(c);
      if (upperCase) {
        robot.keyPress(KeyEvent.VK_SHIFT);
      }
      c = Character.toUpperCase(c);
      robot.keyPress(c);
      robot.keyRelease(c);
      if (upperCase) {
        robot.keyRelease(KeyEvent.VK_SHIFT);
      }
    }
    robot.delay(DELAY);
  }

  private static void sendSpecialCharacter(Robot robot, int... keys) {
    for (int i = 0; i < keys.length; i++) {
      robot.keyPress(keys[i]);
    }
    for (int i = keys.length - 1; i >= 0; i--) {
      robot.keyRelease(keys[i]);
    }
    robot.delay(DELAY);
  }

  public static void main(String[] args) {
    final String TEKST = "to jest test";
    final String FILENAME = "test.txt";
    final String NOTEPAD = "C:\\Windows\\notepad.exe";
    try {
      Runtime.getRuntime().exec(NOTEPAD);
      Robot robot = new Robot();
      sendText(robot, TEKST);
      sendSpecialCharacter(robot, KeyEvent.VK_CONTROL, KeyEvent.VK_S); // <CTRL/S>
      sendText(robot, FILENAME);
      sendSpecialCharacter(robot, KeyEvent.VK_ALT, KeyEvent.VK_Z); // <ALT/Z>
    } catch (IOException | AWTException e) {
      e.printStackTrace();
    }
  }

}

Do problemu można podejść jeszcze inaczej: najpierw utworzyć plik z zawartością, a następnie wczytać go do notatnika. Wówczas kod będzie trochę prostszy:

import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.swing.filechooser.FileSystemView;

public class NotapadRunner2 {
  public static void main(String[] args) {
    final String TEKST = "to jest test";
    final String FILENAME = "test2.txt";
    final String NOTEPAD = "C:\\Windows\\notepad.exe";

    // katalog domyślny dokumentów
    String dir = FileSystemView.getFileSystemView().getDefaultDirectory().getPath();

    // utworzenie pliku z zawartością
    Path path = Paths.get(dir, FILENAME);
    try (Writer out = Files.newBufferedWriter(path)) {
      out.write(TEKST);
    } catch (IOException e) {
      e.printStackTrace();
    }
    // uruchomienie notatnika
    try {
      Runtime.getRuntime().exec(NOTEPAD + " " + dir + File.separatorChar + FILENAME);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

 

 

 

 

 

 

  • Odpowiedział
  • @ | 05.01.2016
  • TRENER MODERATOR ALTKOM AKADEMII
  • 0

Dziękuję, na pewno wykorzystam.

Oto moja propozycja sprawdzenia aktywnego okna. Proszę o weryfikacje i poprawę ;)

Konieczny import https://github.com/java-native-access/jna#download jna.jar i jna-platform.jar

package EnumeratorWindows;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinUser.WINDOWINFO;
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;

public class TryWithHWND {

	public static void main(String[] args) {

//		Przełącz na jakieś okno, program poczeka 5[s]
		try {
			Thread.sleep(5000);

		} catch (InterruptedException ie) {
			ie.printStackTrace();
		}

		final User32 user32 = User32.INSTANCE;
		final WINDOWINFO test = new User32.WINDOWINFO();
		user32.EnumWindows(new WNDENUMPROC() {
			int count = 0;

			public boolean callback(HWND hWnd, Pointer arg1) {
				char[] windowText = new char[512];
				user32.GetWindowText(hWnd, windowText, 512);
				String wText = Native.toString(windowText);
				RECT rectangle = new RECT();
				user32.GetWindowRect(hWnd, rectangle);

				if (wText.isEmpty() || !(User32.INSTANCE.IsWindowVisible(hWnd) && rectangle.left > -32000)) {
					return true;
				}
				
				user32.GetWindowInfo(hWnd, test);
				String active;
				if (test.dwWindowStatus == 1) {
//					W powiązaniu z nazwą okna daje jednoznaczną informację o fokusie, taką mam nadzieję ;)
					active = "Tak";
				} else {
					active = "Nie";
				}
				System.out.println("Znalazłem okno: " + hWnd + ", numer: " + ++count + " ,tytuł okna: " + wText + " ,aktywne: " + active);
				return true;
			}
		}, null);
	}
}

 

Michał_Kocot
  • Odpowiedział
  • @ Michał_Kocot | 06.01.2016
    • 1
    • 0
    • 1
  • 0

Wygląda, że działa poprawnie :-)

Jednak nasuwa mi się jeszcze jeden problem...

Wyobraźmy sobie następujący scenariusz:

  1. sprawdzam, że właściwe okno ma fokus,
  2. rozpoczynam programowe wprowadzanie tekstu,
  3. podaję nazwę i zapisuję plik.

Jednakże nie mamy gwarancji, że po sprawdzeniu fokusa w pkt. 1 ktoś nie wybrał innego okna. Jeśli zmiana okna nastąpiła po 1, a przed lub po 2, to mamy kłopot. Potrzebny jest listener informujący o każdej zmianie fokusa - nie wystarczy jednorazowe sprawdzenie na początku.

Dlatego proszę rozważyć, czy nie byłoby lepszym podejściem zrealizowanie całości w Javie, zamiast odwoływać się do zewnętrznych aplikacji Windows. Java umożliwia tworzenie aplikacji graficznych w oparciu o AWT lub Swing. Komponent graficzny umożliwiający wprowadzanie tekstu (wielowierszowy) to TextArea (JTextArea w Swingu). Tu mielibyśmy większą kontrolę...

  • Odpowiedział
  • @ | 07.01.2016
  • TRENER MODERATOR ALTKOM AKADEMII