小编典典

Java搜索屏幕上的文本字段

java

我正在尝试创建一个程序,该程序可以自动在屏幕上搜索文本字段,然后在该文本字段中重复输入一个单词。有没有可以找到文本字段的类?还是可以找到文本字段的任何方式?因为我知道Robot类可以键入文本,所以我只需要将光标移到文本字段上并使用mousePress()和mouseRelease()方法即可。

谢谢


阅读 222

收藏
2020-11-01

共1个答案

小编典典

我无法直接为您提供解决方案,但是我弄乱了一些代码,也许可以为您指明正确的方向。

您可能知道,Java在JVM中运行。这使其可以在任何操作环境中执行。每种操作环境(Windows,Mac等)都有其自己的系统,用于处理编辑框并将焦点设置在右侧窗口等。以下示例代码仅设计用于Windows,不符合Java语言的精神。正如Adriaan指出的那样,这类事情还有其他语言,但仅在Java上(有可能)就可以做到。

在Windows中,您必须了解如何管理所有活动窗口,以及所看到的所有内容(包括编辑框)都在考虑Windows
OS的“窗口”。我并不真正了解事物的幕后运作方式,因此我无法提供更多信息。在诸如C ++之类的本地语言中,Windows OS
API提供了一些功能,这些功能可用于实现您的目标。即,EnumWindows()EnumChildWindows()GetClassName(),和SetForegroundWindow()。您可以通过搜索MSDN文档库找到有关如何在本机语言中使用这些功能的文档。

因此,您需要能够从Java调用这些函数。通常情况下,无法调用这些本机方法。但是,有一个库可以帮助您:JNA库。JNA代表Java Native
Access,它使您可以使用前面提到的闪亮的新功能。

因此,为了以母语实现您的目标,通常会从调用EnumWindows()返回操作系统知道的所有父窗口列表开始。此列表将包含父窗口的窗口句柄-名为“
MSN”,“ Eclipse”,“ Microsoft
Office”等的窗口。作为父级,这些窗口中的每个都有子级。在此子级列表中,您会找到所需的“控件”:Edit控制。现在,许多应用程序对文本框使用不同的库和非标准的东西-
即Pidgin,我用其测试了一些相关代码的消息传递应用程序,具有每个名为“
gdkWindowChild”的控件,它不能确切告诉我们哪个控件实际上是EditBox或否则是一个允许我们输入文本的地方。那是你想法的主要问题;您无法始终准确地说出您希望将焦点放在哪个控件上,以便您可以输入文本。无论如何,我们将继续:

在找到相关的“父”窗口之后EnumWindows(),调用EnumChildWindows()将给我们所有属于父级的子窗口和其他“控件”(包括潜在的编辑框)。EnumChildWindows()为找到的每个子窗口调用一个回调函数,因此很容易在子窗口列表中“搜索”-使用GetClassName()查找控件的名称-
潜在地找到所需控件的HWND(窗口句柄) 。

找到正确的编辑框HWND后(当然,这是问题的总体范围,这是最困难的部分),只需简单调用SetForegroundWindow(targetHWND)即可将控件置于最前面,并将光标置于准备就绪状态。类型的编辑框。

这是我为帮助您入门而编写的一些示例代码。此代码将使用遍历所有活动窗口EnumWindows(),然后EnumChildWindows()在每个父窗口上调用,打印出找到的所有控件。请注意,此代码需要运行JNA库。

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.examples.win32.W32API.HWND;
import com.sun.jna.examples.win32.W32API.LPARAM;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;


public class IterateChildWindows {
    public interface User32 extends StdCallLibrary {
        User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS);

        HWND FindWindow(String lpClassName, String lpWindowName);
        int GetWindowRect(HWND handle, int[] rect);
        int SendMessage(HWND hWnd, int msg, int wParam, byte[] lParam); 
        HWND FindWindowEx(HWND parent, HWND child, String className, String window);

        boolean EnumWindows(WNDENUMPROC lpEnumFunc, Pointer arg);
        boolean EnumChildWindows(HWND parent, WNDENUMPROC callback, LPARAM info);

        interface WNDENUMPROC extends StdCallCallback {
            boolean callback(HWND hWnd, Pointer arg);
        }

        int GetWindowTextA(HWND hWnd, byte[] lpString, int nMaxCount);
        long GetWindowLong(HWND hWnd, int index);
        boolean SetForegroundWindow(HWND in);
        int GetClassNameA(HWND in, byte[] lpString, int size);
    }

    public static void main(String[] args) {        
        User32.INSTANCE.EnumWindows(new User32.WNDENUMPROC() {
            public boolean callback(HWND hWnd, Pointer userData) { // this will be called for each parent window found by EnumWindows(). the hWnd parameter is the HWND of the window that was found.
                byte[] textBuffer = new byte[512];
                User32.INSTANCE.GetWindowTextA(hWnd, textBuffer, 512);
                String wText = Native.toString(textBuffer);
                System.out.println("Window found: " + wText);

                // now call EnumChildWindows() giving the previously found parent window as the first parameter
                User32.INSTANCE.EnumChildWindows(hWnd, new User32.WNDENUMPROC() {
                    public boolean callback(HWND hWnd, Pointer userData) { // this is called for each child window that EnumChildWindows() finds - just like before with EnumWindows().
                        byte[] textBuffer = new byte[512];
                        User32.INSTANCE.GetClassNameA(hWnd, textBuffer, 512);
                        System.out.println(" - Found sub window / control class: " + new String(textBuffer).trim());
                        return true;
                    }
                }, null);
                return true;
            }
        }, null);
    }
}

这是此代码提供的输出摘录:

Window found: Pidgin
 - Found sub window / control class: gdkWindowChild
 - Found sub window / control class: gdkWindowChild
 - Found sub window / control class: gdkWindowChild
 - Found sub window / control class: gdkWindowChild
Window found: Malwarebytes Anti-Malware
 - Found sub window / control class: Static
 - Found sub window / control class: Static
 - Found sub window / control class: Button
 - Found sub window / control class: Button
 - Found sub window / control class: Button

通过PostMessage()和直接将消息发送到HWND控件SendMessage(),例如,发送到MalwareBytes
Button类,将触发程序本身的按钮按下,这与SetForegroundWindow()将编辑框样式控件置于最前面的方式非常相似,从而使您能够输入。有趣的东西玩:)

如果您想形象地表达我在说“父母”,“孩子”和“控制”时的意思,则该程序会有所帮助:Control
Viewer
。它可以显示每个控件,并在应用程序窗口以及其他功能中突出显示它-非常有用的工具。

抱歉,如果该帖子离开了Java提供的舒适区域,但在这样的一般范围内,实际上没有其他方法可以实现您的目标。

我希望至少已向您展示了实现目标所需的条件,并为您指明了正确的方向。对于本机Windows
API来说,我不是上帝,所以在某些地方我可能是错的,但是代码确实可以工作。祝好运 :)

2020-11-01