Python:访问外部进程信息
我正在尝试用Python来管理进程,虽然用C++可能会更好。我使用的是Python 2.7,操作系统是Ubuntu 14.04。
我想实现的目标总结如下:
- 向正在运行的进程发送动作(不是信号)//与进程的用户界面互动
- 读取内存地址的值
我的目的是创建一个脚本来管理其他软件,类似于Selenium对浏览器的操作,但可以用于任何程序。也许用Python的subprocess来执行进程,可以让我管理进程的用户界面。
发送动作/与运行中的进程互动
目前我在Linux上使用psutil
编写这个脚本。我知道在Windows上有一些库,比如pywin
或pywindll
。
我想管理一个进程,比如任何有用户界面的软件(Skype、Gedit、Firefox等),我想知道是否可以发送一个动作来点击一个按钮。
我不想控制电脑上的鼠标,因为假设这个窗口被其他窗口遮住了:
- 一旦我在脚本中得到了这个进程,是否可以发送一个点击动作到用户界面的按钮?(或者在某个文本框中输入内容)
我正在使用psutil
来获取进程,并且有很多选项,比如:
- 获取内存映射
- 获取进程的线程
- 结束进程
- CPU使用率
- 等等
但这些操作似乎都不是我想要的,真正想要的是与进程的用户界面互动...
我想实现的目标真的可能吗?
最简单的解决方案是发送键盘输入和鼠标点击吗?
读取内存地址的值
我在Linux上使用scanmem
来查找某个变量的内存地址,一旦找到我想要的内存地址,我想在Python中使用这个地址来获取存储在该地址的值。
我找到的最接近的方法是使用ctypes
,类似于:
from ctypes import string_at
from sys import getsizeof
mem_address = 0x7c3f
value = string_at(id(mem_address), getsizeof(mem_address))
- 这是在Python中访问内存地址的最简单方法吗?
- 可以实时修改/更新这个数字吗?
- 我该如何用用户界面识别内存地址?
我在想,一个程序在执行时必须将其用户界面发送给操作系统,是否可以用Python来“捕获”这个界面,并重定向到操作系统?
类似于通过Python执行软件,这样可能就能直接管理用户界面。
2 个回答
我想我有一个方法,可以让你从一个程序中读取另一个程序的变量。为了简单起见,假设你想在两个用C++写的程序之间建立通信,分别叫做程序A和程序B。针对你的问题,我们可以设想程序A想要访问程序B中的一个变量的值。
在这种情况下,我想到可以使用Boost的进程间通信。Boost Interprocess是Boost库中的一个部分,它允许你通过共享内存在两个进程之间进行通信。你可以使用这个库中的消息队列。想了解更多信息,可以查看这里:
http://www.boost.org/doc/libs/1_56_0/doc/html/interprocess.html
回到这个例子,你需要写一些代码来支持在进程之间读取和写入变量。假设现在我们只能读写数组和标量值。所以你需要维护一个数据结构(最好是一个映射),将变量的名称映射到内存中的位置。大概是这样的:
#include <map>
//Somewhere in your program you have a variable
int my_var = 5;
//Declare a map of string mapping to 64 bit pointers
std::map<std::string, long long> var_map;
//At any point you decide to register the reference of this value
var_map["my_var"] = (long long) &my_var;
//Now that you have registered this value,
//you can access it according to the name and
//type cast it as well to a data structure that you like
int *ptr = (int *) var_map["my_var"];
//Now you can play around with this:
*ptr = 1024;
希望你能明白我想表达的意思,这种类型的代码会存在于程序B中。这样做的原因是程序A可以向程序B发送命令,告诉它我想读取一个叫“my_var”的变量。
接下来就是实际通信的部分。在Boost Interprocess中,你可以打包结构体,这些结构体可以是读取命令,比如这样:
typedef ReadCommand {
char *var_name;
int read_bytes;
}
请查看Boost的文档,了解如何设置共享内存实例,因为一旦你设置好了,就可以发送这样的命令:
//Code in program A
ReadCommand read_command;
read_command.var_name = "my_var";
read_command.bytes = 4;
try {
//Need to declare message_queue, please see doc in Boost
message_queue_A->send(&read_command, sizeof(ReadCommand), 0);
} catch (boost::interprocess::interprocess_exception &ex){
//Handle exception
}
再回到程序B,你可以有这样的代码来接收消息:
//Best to have struct definition in shared header file
ReadCommand read_command_B;
int some_priority;
boost::interprocess::message_queue::size_type size_of_data_recvd;
message_queue_B->receive(&read_command_B, sizeof(ReadCommand), size_of_data_recvd, some_priority);
//use information in read_command_B
//to access var_map then use another
//message queue to send back data to
//Program A which will be expecting
//some information from program B.
总之,一旦你完成了这些,你就可以将Python与Boost Python库结合使用。希望这些内容对你有帮助。试试看,如果你遇到任何问题,请告诉我。这不是一个精确的实现,而是一个可以解决你读取变量问题的思路。
至于发送动作的问题,我不太确定如何与另一个进程中的用户界面进行交互。通常,供应商会向程序员提供API,用于与UI元素进行交互。底层发生的事情很难理解和操作,因为在大多数情况下,它们是闭源的。如果你能访问API中的源代码,那就另当别论了。你可以使用类似上面提到的概念,向另一个程序中的某个位置写入数据,从而触发UI中的事件。
我喜欢你的想法 :D 界面自动化真棒
关于这个问题,按照我的理解,所有能和程序的图形界面互动的软件,都是基于计算机视觉和光学字符识别(OCR)或者直接读取内存来获取界面对象模型。后者可能不太通用,因为不同的界面工具包和构建界面的方法会有不同的底层模型——这可能比计算机视觉加OCR更麻烦。
如果你想看看已经为这个目的开发的一些工具,可以去看看这个维基百科列表。你已经知道Selenium了,但还有其他的工具——我查过的有AutoIt和Sikuli,我想用它们做一个类似的项目,使用Python。(AutoIt像BASIC一样——真糟糕——而且只支持Windows,但Sikuli似乎和Python有关,并且是跨平台的——我很久以前查过它们,所以不记得细节了。)
好消息是,Python有很不错的计算机视觉和OCR模块。我个人推荐simplecv,它可以包装OpenCV和其他计算机视觉软件。虽然我没有特别推荐的OCR模块,但我在寻找模块时,觉得python-tesseract是最好的。
一般来说,做法是先拍一张界面的快照(graphicsmagick可以很好地做到这一点,还有Python的封装),然后用计算机视觉找出界面元素的位置,接着用OCR读取标签,这样就能得到窗口上内容的模型。然后,你可以给你的脚本指示该做什么和何时做,这些都是基于它在界面上的位置。因为Python可以发送鼠标和键盘事件,所以你就可以实现自动化了。你甚至可以使用minidom
模块来为你的代码创建一个更简单的对象模型。
顺便提一下,CV+OCR的方法也被一个与《炉石传说》相关的应用使用,它会拍摄游戏的快照并读取分数,然后为玩家跟踪这些分数,以便他们可以制定指标。这种方法比看起来要轻便和简单得多——我查看了代码,尽管背后有复杂的技术,但理解起来相当容易。