Java - rozpoznawanie pola tekstowego

Może ma ktoś pomysł, jak zrobić coś takiego - program (Swing), który wykona jakąś akcję - chociażby wyświetlenie napisu "udało się" na Formie - w momencie kiedy użytkownik w jakimś innym programie kliknie pole edycji tekstu. Podsumowując:

  1. Uruchamiam napisany przeze mnie program
  2. Klikam w pole edycji tekstu (np. we Firefoxie kliknę w pole dodawania odpowiedzi)
  3. W moim programie wyświetla się napis - udało się.

1 rok, 10 miesięcy temu | edytowane przez: Manveru 4224121

  • Nie powinno to być trudne pod Windows.

    Potrzebujesz kawałka w C [najprościej w C pisać], który użyje funkcji WinAPI getFocus(), która zwróci Ci uchwyt do okna które ma focusa, teraz prosząc o klasę tego okna przy pomocy GetClassName, teraz jeżeli nazwa klasy to jeśli dobrze pamiętam EditBox to masz właśnie pole tekstowe.

    Ten kod w C robisz JNA'owym albo JNI'owym i powinno działać.

    Jak lubisz możesz te funkcje wołać przez JNA, wtedy nie potrzebujesz żadnego kawałka w C.

    Inna sprawa po co Ci to ;-) Bo możliwe, że da się to zrobić o wiele prościej.

    Wszystko w pętli, która co powiedzmy 100 ms woła to getFocus()

    [Późniejszy Edit ;-)]

    Nie jest to aż tak proste, bo getFocus() zwraca null, dlatego trzeba użyć GetGUIThreadInfo() a tutaj mam problemy bo coś w parametrach mieszam. Na razie mój kod wygląda tak, może Ci to coś pomoże (klasa zowie się JNAToster), teoria mówi, że jeżeli dobrze się wszystko woła to w lpgui.hwndFocus powinien być uchwyt do okna, która ma focus:

    import com.sun.jna.Native;
    import com.sun.jna.Pointer;
    import com.sun.jna.Structure;
    import com.sun.jna.win32.StdCallLibrary;
    
    public class JNAToster {
        public interface Kernel32 extends StdCallLibrary {
            int GetLastError();
        }
        public static class RECT extends Structure {
              long left;
              long top;
              long right;
              long bottom;
            }
    
          public static class GUITHREADINFO extends Structure {
                public int cbSize = size();
                public int flags;
                Pointer hwndActive;
                Pointer hwndFocus;
                Pointer hwndCapture;
                Pointer hwndMenuOwner;
                Pointer hwndMoveSize;
                Pointer hwndCaret;
                RECT rcCaret;
            }       
        public interface User32 extends StdCallLibrary {    
    
            Pointer GetFocus();
            boolean GetGUIThreadInfo(Pointer idThread, GUITHREADINFO lpgui);
            Pointer GetForegroundWindow();
            int GetWindowThreadProcessId(
                      Pointer hWnd,
                      Pointer lpdwProcessId
                    );
    
        }
    
        public static void main(String[] args) {
            User32 user32 = (User32)Native.loadLibrary("user32", User32.class);
            Kernel32 kernel32 = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class);
            while (1==1) {
                GUITHREADINFO lpgui = new GUITHREADINFO();
                int id = user32.GetWindowThreadProcessId(user32.GetForegroundWindow(), null);
                System.out.println(id);
                lpgui.cbSize=lpgui.size();
                user32.GetGUIThreadInfo(null, lpgui);
                System.out.println("err="+kernel32.GetLastError());
                System.out.println(lpgui.hwndFocus);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ie) {
    
                }
            }
        }
    }
    

  • To będzie pierońsko trudne do osiągnięcia, jako że Swing w Javie obsługuje swój mechanizm zdarzeń, niezależnie od systemu operacyjnego na którym działa program. A różnice między Windows a Linux są w tej materii przeogromne (nie wiem jak to działa w MacOS X). W przypadku Windows w skrócie: Twój program musiałby podsłuchiwać kolejkę zdarzeń na poziomie systemu i dopasować zdarzenie pochodzące od określonej kontrolki (jej okna) dotyczące przejęcia skupienia (ang. focus). Problemem, o który potykasz się w pierwszej kolejności, jak zapiąć maszynę wirtualną Javy do słuchania kolejek zdarzeń w Windows - wydaje się, że bez fragmentu kodu w C/C++ obsługującego tą funkcjonalność i gadającego z Javą się nie obejdzie. Choć tu się mogę mylić, bo po sprawdzeniu okazuje się że istnieje JNA - może tu pomóc. Ale na WINAPI trzeba się poznać.

    Pod Linux jest jeszcze bardziej pogmatwana sytuacja, bo aplikacje kooperują z serwerem X na poziomie wywołań z bibliotek X'ów. Nie ma zdarzeń systemowych, które można podsłuchać. Jakieś mechanizmy przejmowania tego co się w X'ach dzieje na pewno istnieją, bo w końcu programy nagrywające to co się dzieje na ekranie istnieją, choć one nie muszą identyfikować nie swoich kontrolek.

  • A może dało by się to zrobić w ten sposób (sposób na Windows'a):

    Korzystając z faktu, że uruchamiasz jeden i ten sam cudzy program - tutaj weźmy na ruszt np . Total Commander. W tym programie za pomocą skrótu klawiszowego CTRL+F pokazuje się okienko "Connect to FTP serwer". Następnie w tym okienku możemy edytować istniejące lub dodawać nowe serwery FTP z którymi się łączymy. Przypuśćmy, że mamy już jakiś dodany.. więc zaznaczamy go i naciskamy "Edit...". Następnie pokazuje się nam okienko "FTP connection details". W tym momencie uruchamiamy program o nazwie "vaSpy" (Jest to program który wyświetla szczegółowe infromacje o każdym oknie/kontrolce nad którym znajduje się kursor myszki. I teraz po najechaniu kursorem w programie Total Commander na pole tekstowe o etykiecie "User name" w oknie programu vaSpy przy etykiecie tekstowej "Window Text" pokaże nam się tekst jaki się znajduje w programie TC. I teraz robimy tak:

    • przy etykiecie tekstowej "Window Handle" programu vaSpy wyświetla nam się np. 900D6h. Jest to uchwyt tego okna - "okna" bo wszystko w Windowsie tj każda kontrolka to jest okno
    • zapisujemy sobie ten numer
    • tworzymy własny program a w nim po numerze uchwytu (tu: 900D6h) odszukujemy to okno za pomocą funkcji WinAPI i odczytujemy z niego treść "Window Text"
    • co pewien określony interwał czasu sprawdzamy w tym oknie czy ta treść uległa zmianie - jeżeli tak to nasz program ma odpowiednio zareagować.

    Należy jednak pamiętać, że po każdym stworzeniu okna przez program jego uchwyt posiada inną wartość. Ale da się takie okienko wyszukać tzn. za pomocą WinAPI da się sprawdzać czy okno posiada jakiś konkretny tytuł/treść wewnątrz siebie np. jeżeli okno posiada text "Total Commander Ultima Prime 4.9" to już wiemy, że szukamy okienek wewnątrz tego okna.

    Sposób ten wykorzystuje fakt zmiany zawartości tekstu w polu tekstowym. Ale być może istnieje funkcja WinAPI która sprawdza czy okno o podanym uchwycie ma fokus - czyli, że zostało klikniete.

    Oczywiście w/w sposób nie jest sposobem Javowym tylko raczej C/ASM/WinAPI jednak można by było skompilować taki kod do biblioteki i używac ją wewnątrz programu javowego.

    p.s. ten sposób z odczytywaniem tekstu wewnątrz kontrolki działa w Windowsie od zarania dziejów również dla pól tekstowych które przechowują hasła w formie gwiazdek.

  • Taki sobie pomysł, bo tytuł na belce tytułu się może zmienić, a niektóre programy nie muszą mieć tego tytułu. Lepiej odczytywać nazwę klasy [nie takiej "obiektowej" klasy, a klasy okna i można to spokojnie zrobić w Java'ie z JNA, no i nie pomoże to wcale odczytać tego jaki element tego okna ma focus]

    tzn., tak, tylko, że w jednym oknie windowsowym od programu może być wiele kontrolek (okien) typu pole do wpisywania tekstu i wszystkie one mogą być jednej i tej samej klasy. Dlatego właśnie jeżeli używamy jednego ściśle określonego programu to można wyszukiwać uchwyt kontrolki (okna) za pomocą tekstu w nim zawartego bądź w kontrolki znajdującej się obok.

    Co do fokusu to chodziło mi raczej o ustalenie czy w kontrolce (oknie) nastąpiła zmiana (po przez zmiane jej zawartości) co sugerowało by, że okno (kontrolka) dostała fokus.

Zaloguj się, aby dodać swoją odpowiedź