java获取其他应用程序的窗口控件

该文章转自:https://me.csdn.net/devmiao 大神的回答。

转自:https://ask.csdn.net/questions/713450 问答。

比如现在有一个桌面应用。我现在要抓取这个桌面应用输入框或者其他控件的内容

问题背景:
我的毕业设计中需要在Windows平台上面跨进程操作窗口。实际上是获取浏览器上面的网页中的文本框元素,还有windows32窗体上面的编辑框。然后进行自动填值等的操作。

我能想到的一共有这么几种方法:

使用C#编写窗体应用程序,然后使用WebBrowser浏览器控件或者嵌入其他应用程序窗口。如果使用WebBrowser控件,只能强制用户使用该C#应用程序上网,影响用户体验,不切实际。如果使用嵌入其他应用程序窗口的方式,其实就转化为了跨进程获取窗口的方法了。

使用浏览器插件的方式,针对不同浏览器编写不同插件,然后让用户安装。当浏览器页面载入后,使用驻留程序(这是我毕设的核心进程)向浏览器发消息,执行浏览器插件中的JS代码操作网页DOM元素。但是缺点是需要编写很多插件,且调试起来,真正执行起来很艰难。

先使用远程线程注入到目标进程的线程空间,创建一个虚拟线程,然后执行这个虚拟线程,向拥有这个窗口的界面线程发送消息。实际上这个方法和上面的方法大同小异。只不过进程注入行为会被用户系统的安全机制检测到,类似360安全卫士这种神经质的安全软件会让用户把我们的程序查杀掉。另外需要针对各种浏览器,各种程序窗体做特定的分析处理,代价太大,而我只不过是完成一个毕设,没必要用牛刀吧。

使用模拟用户操作方式。先拿简单的方法说,很多脚本语言例如在Windows上面的VBS脚本执行时会启动WScript驻留进程,使用VBS的 sendKey 命令可以模拟用户的输入,甚至VBS能模拟用户鼠标的点击。还可以使用Python,JS(需要先让用户下载python)等都可以。他们的核心其实都是调用Windows系统API来完成功能,从结构上来看都是要运行一个本地即时解释器,它可以调用WindowsAPI,然后解释脚本执行操作。再说深层次一点就是先获取目标窗口的句柄,然后对该窗体的消息处理队列发送WM_SET_TEXT,WM_GET_TEXT,WM_EXIT等各种消息。
本文考虑到毕设需要具有跨平台的特性,并且最好能够兼容各种不同版本的Windows。因此使用Java语言的JNA包提供的方便的功能调用WindowsAPI。而是用JNI也可以。只不过还要编写DLL,编译再加调试,会浪费很长时间。如果不是针对特定问题,使用成熟的JNA况且会帮助你解决低层调用的各种问题,何乐而不为呢。

摘取一些JNA简介:
JNA提供Java程序轻松访问本机共享库,而不需要编写任何Java代码 - 不需要JNI或本机代码。这个功能与Windows的Platform / Invoke和Python的ctypes类似。
JNA允许您使用Java的方法调用来直接调用本机函数。调用看起来就像本机代码中的调用一样。大多数的方法调用不需要特殊的处理或配置。
JNA使用一个小的JNI库存根来动态调用本地代码。开发人员使用Java接口描述目标本机库中的函数和结构。这使得很容易利用本机平台功能,而不会导致为多个平台配置和构建JNI代码的高开销。
因此,JNA提供了相比较性能来说更关注平台适应以及便利性,节省使用者需要面对多版本,多平台开发程序的时间。

除了Windows, JNA还支持多种其他的平台。例如ARM,安卓,Linux等。
JNA可以通过Maven包管理下载。

		<dependency>
			<groupId>net.java.dev.jna</groupId>
			<artifactId>jna</artifactId>
			<version>4.4.0</version>
		</dependency>
		<dependency>
			<groupId>net.java.dev.jna</groupId>
			<artifactId>jna-platform</artifactId>
			<version>4.4.0</version>
		</dependency>

如果不适用Maven管理包,可以自己下载下面的两个包放到项目中:

http://repo1.maven.org/maven2/net/java/dev/jna/jna/4.4.0/jna-4.4.0.jar
http://repo1.maven.org/maven2/net/java/dev/jna/jna-platform/4.4.0/jna-platform-4.4.0.jar

这个是必备的参考文档:

http://java-native-access.github.io/jna/4.4.0/javadoc/

JNA的GitHub地址:

https://github.com/java-native-access/jna#readme

为了示范其简单性,看下面的代码。

import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinUser;
/**

Created by lenovo on 2017/4/27.
使用winID来获得窗口的类型和标题,然后发送消息或者其他操作
*
*/
public class jnaTest {
public static void main(String[] args) {

    HWND hwnd = User32.INSTANCE.FindWindow
        (null, "QQ"); // 第一个参数是Windows窗体的窗体类,第二个参数是窗体的标题。不熟悉windows编程的需要先找一些Windows窗体数据结构的知识来看看,还有windows消息循环处理,其他的东西不用看太多。
if (hwnd == null) {
    System.out.println("QQ is not running");
}else{
    User32.INSTANCE.ShowWindow(hwnd, 9 );        // SW_RESTORE
    User32.INSTANCE.SetForegroundWindow(hwnd);   // bring to front

    //User32.INSTANCE.GetForegroundWindow() //获取现在前台窗口
    WinDef.RECT qqwin_rect = new  WinDef.RECT();
    User32.INSTANCE.GetWindowRect(hwnd, qqwin_rect);
    int qqwin_width = qqwin_rect.right-qqwin_rect.left;
    int qqwin_height = qqwin_rect.bottom-qqwin_rect.top;

    User32.INSTANCE.MoveWindow(hwnd, 700, 100, qqwin_width, qqwin_height, true);
    for(int i = 700; i > 100; i -=10) {
        User32.INSTANCE.MoveWindow(hwnd, i, 100, qqwin_width, qqwin_height, true);   // bring to front
        try {
            Thread.sleep(80);
        }catch(Exception e){}
    }
    //User32.INSTANCE.PostMessage(hwnd, WinUser.WM_CLOSE, null, null);  // can be WM_QUIT in some occasio
}
}

//在Windows中,User32.dll文件拥有大量的操作用户界面的API。可以看到JNA在包命名上也遵照了DLL的命名规律。
如果我们事先打开QQ程序的登陆界面,当我们运行上面的程序时,就会将QQ登陆窗体置于前台显示同时将他从屏幕的右边移动到屏幕的左面。
另外,学过windows编程的都知道,一个windows32程序一般都会有自己独有的窗体类,即叫做 Window Class,例如 windows下的图片查看器的主窗口类为"Photo_lightweight_Viewer", 记事本窗口的窗体类叫做"Notepad"。一个窗口类是一个窗体风格,程序中可以定义多个窗体类。当然,WIndows32程序也可以使用其他程序的窗体类。上面的 FindWindow 函数的第一个参数可以传入一个窗体类名,这样可以缩小低层JNA调用 FindWindowEX 函数查找的范围。对于Windows窗体的信息,可以使用 WinID 这个软件来查询。VS编程的同学可以使用Spy++工具查看。

下面来解决我上面说的主要问题:

import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;

/**

Created by lenovo on 2017/4/27.
使用winID来获得窗口的类型和标题,然后发送消息或者其他操作
*
*/
public class jnaTest {
public static void main(String[] args) {

WinDef.HWND hwnd = User32.INSTANCE.FindWindow
        (null, "QQ"); // 第一个参数是Windows窗体的窗体类,第二个参数是窗体的标题。不熟悉windows编程的需要先找一些Windows窗体数据结构的知识来看看,还有windows消息循环处理,其他的东西不用看太多。
if (hwnd == null) {
    System.out.println("Excel is not running");
}
else{
    User32.INSTANCE.ShowWindow(hwnd, 9 );        // SW_RESTORE
    User32.INSTANCE.SetForegroundWindow(hwnd);   // bring to front

    String username = "yourQQnumber";
    for(Character c: username.toCharArray())
    sendChar(c);
}
}

static WinUser.INPUT input = new WinUser.INPUT( );
static void sendChar(char ch){

input.type = new WinDef.DWORD( WinUser.INPUT.INPUT_KEYBOARD );
input.input.setType("ki"); // Because setting INPUT_INPUT_KEYBOARD is not enough: https://groups.google.com/d/msg/jna-users/NDBGwC1VZbU/cjYCQ1CjBwAJ
input.input.ki.wScan = new WinDef.WORD( 0 );
input.input.ki.time = new WinDef.DWORD( 0 );
input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR( 0 );
// Press
input.input.ki.wVk = new WinDef.WORD( Character.toUpperCase(ch) ); // 0x41
input.input.ki.dwFlags = new WinDef.DWORD( 0 );  // keydown

User32.INSTANCE.SendInput( new WinDef.DWORD( 1 ), ( WinUser.INPUT[] ) input.toArray( 1 ), input.size() );

// Release
input.input.ki.wVk = new WinDef.WORD( Character.toUpperCase(ch) ); // 0x41
input.input.ki.dwFlags = new WinDef.DWORD( 2 );  // keyup

User32.INSTANCE.SendInput( new WinDef.DWORD( 1 ), ( WinUser.INPUT[] ) input.toArray( 1 ), input.size() );
}
}
  • 注意,使用前需要先选定目标焦点。

参考网站:
http://www.rgagnon.com/topics/java-jni.html 这个网站上有几个JNA的实例,熟悉Windows窗体编程的朋友们看起来应该很容易。
https://github.com/java-native-access/jna#readme
http://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html 这个是新加坡理工大学的网站,想入门JNI的可以去看看。
http://stackoverflow.com/questions/28538234/sending-a-keyboard-input-with-java-jna-and-sendinput 包含sendkey方法的使用
https://coderanch.com/t/635463/java/JNA-SendInput-function 包含sendkey方法的使用

感谢强大的谷歌

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Python的subprocess模块来控制应用程序。subprocess模块允许你在Python脚本中启动新的进程,并与这些进程进行交互。下面是一个使用subprocess模块来启动应用程序的示例代码: ``` import subprocess # 启动应用程序 subprocess.Popen(['path/to/your/application.exe']) # 关闭应用程序 subprocess.Popen(['taskkill', '/F', '/IM', 'application.exe']) ``` 在上面的示例代码中,我们使用Popen()函数来启动应用程序,并使用taskkill命令来关闭应用程序。你可以根据你的应用程序类型和操作系统的不同,来选用不同的命令来启动和关闭应用程序。 ### 回答2: Python是一种高级编程语言,可以用来开发各种类型的应用程序。通过使用Python,我们可以灵活地控制应用程序的运行。 首先,要控制应用程序,我们可以使用Python的基本语法和控制流程来编写代码。我们可以定义变量、使用条件语句和循环语句,以及调用函数和方法。这些工具可以帮助我们控制程序的执行顺序和逻辑。 其次,Python提供了很多有用的库和模块,可以帮助我们更方便地控制应用程序。比如,我们可以使用Tkinter库来创建图形用户界面(GUI)应用程序,通过设计窗口、按钮和文本框等控件,来实现与用户的交互。另外,我们也可以使用socket和HTTP库来实现网络通信,控制应用程序与其他计算机或设备之间的数据传输。 此外,Python还支持多线程和多进程编程,这样我们可以同时执行多个任务,提高应用程序的效率和响应能力。我们可以使用threading和multiprocessing库来创建和管理线程和进程,以及实现线程/进程间的通信与同步。 最后,Python还可以通过调用其他语言编写的库或执行外部命令来控制应用程序。例如,我们可以使用subprocess库执行操作系统命令、调用C/C++编写的动态链接库,或者与JavaJavaScript等其他语言编写的程序进行交互。 总之,Python具备强大的功能和丰富的库支持,可以用来控制各种类型的应用程序。无论是简单的命令行工具,还是复杂的图形用户界面或网络应用,Python都可以提供灵活而便捷的方式来实现控制。 ### 回答3: Python是一种高级编程语言,广泛应用于控制应用程序的开发中。它提供了丰富的库和工具,使得控制应用程序变得简单而灵活。 首先,Python可以用于编写脚本,通过解释器直接执行。开发者可以利用Python的强大函数库,如os、sys等,来实现对应用程序的控制。脚本可以包含一系列命令和逻辑,能够执行特定的操作,如启动和停止应用程序,执行特定的功能等。 其次,Python还可以通过面向对象编程的方式来控制应用程序。开发者可以定义类和对象,将应用程序的不同功能封装成方法和属性。通过调用这些方法和属性,我们可以对应用程序进行控制和操作。这种方式使得代码更清晰、可维护性更强,并能够快速扩展和修改应用程序的功能。 另外,Python还提供了很多用于与系统交互的库,比如subprocess、os等。这些库允许开发者执行外部命令、操作文件系统、处理进程等系统级操作,从而实现对应用程序的控制。 此外,Python还可以通过网络通信来控制应用程序。开发者可以使用Python的网络编程库,如socket、requests等,与应用程序进行远程通信。通过发送特定的请求,我们可以实时控制应用程序的运行状态、获取数据和设置参数等。 综上所述,Python提供了多种方式来控制应用程序,无论是直接执行脚本、通过面向对象编程、系统交互还是网络通信,都可以轻松实现对应用程序的控制。它的简洁、灵活和强大的功能使得Python成为控制应用程序的优秀选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值