如何从QProcess中获取QWidgets并与之交互

How to obtain and interact with QWidgets from QProcess

本文关键字:交互 QWidgets 获取 QProcess      更新时间:2023-10-16

我有Application A,这是一个使用Qt库的第三方Windows应用程序(带有GUI)。

我想写Application B,它将负责启动Application A。我希望Application B也能找到Application B (QWidgets)上的按钮,并向它们发送鼠标输入(单击,双击等)。

我可以通过在QProcess上使用start函数来运行Application A

我如何从我的QProcess实例中执行以下操作:

  1. 获取进程的顶层窗口
  2. 通过名称或(其他可识别的属性)获取小部件
  3. 获取小部件的标题,颜色和坐标(可能还有一些其他数据)
  4. 发送鼠标移动和点击事件到特定坐标
  5. 给一个小部件键盘焦点
  6. 发送键盘按键

注意-我知道如何用Windows API做到这一点,但要求通过Qt的方式。特定的Application A问题不使用本机窗口系统,因此窗口句柄不会显示在spy++或Windows API函数中。

更新1 -似乎无法通过进程的子进程获得任何有意义的对象

为进程获取子部件的尝试:

QProcess* process = new QProcess();
QString program = ""C:\Program Files (x86)\foo\bar.exe"";
process->start(program);
auto widgets = process->findChildren<QWidget*>("", Qt::FindChildrenRecursively);
auto i = widgets.count();
// i = 0

如果我找到类型<QObject*>的孩子,我得到4个结果。我使用metaObject()->className()看到我有两对qwindowspiperreader和QWinOverlappedIoNotifier对象。

更新2 -无法从另一个进程创建/注入窗口

我注意到,当我运行QProcess时,我可以使用Windows API函数来获得顶层窗口(仅限顶层)。我在Qt文档中读到,你可以使用QWindow::fromWinId从另一个进程中的窗口句柄创建一个QWindow。

使用这个函数会抛出一个0xC0000005: Access violation reading location 0x00000000.错误。我没有传入空句柄。我使用reinterpret_cast将HWND转换为WId类型。只有当我事先创建QApplication时,它才会创建一个QWindow。

新的QWindow将没有子窗口(使用window->findChildren<QObject*>("", Qt::FindChildrenRecursively);,我假设创建QWindow不会带来关联的子窗口)

更新3 -我目前正在阅读进程间通信是否可以使用

我遇到了关于Qt中ICP的各种线程,问题和代码片段,到目前为止,我没有看到任何具体表明当其中一个进程是第三方时ICP是可能的。

我已经看到,Squish Gui测试工具允许您查询QWidget属性。

这永远不会像你想的那样工作。

如果您真的希望直接控制其他应用程序,您必须将您的代码注入该应用程序。让我们假设应用程序使用动态链接的Qt。

  1. 构建一个二进制兼容的Qt版本,使用与构建您打算调整的应用程序相同的编译器。

  2. 将应用程序的Qt替换为您的Qt。如果您的代码应该是二进制兼容的,那么一切都应该正常工作。如果没有,说明二进制兼容性不存在,你必须调整一些东西,直到它们正常工作。

  3. 编辑你的Qt,在QApplication构造函数的末尾添加一个钩子来初始化你的代码。这使得提供QApplication的Qt模块依赖于你的代码。

  4. 把你的代码放入一个dll, widget(对于Qt 5)或gui(对于Qt 4)模块现在依赖。

  5. 再次用你的Qt替换应用程序,用钩子启动你的代码。

您的代码当然需要是异步的,并且将通过检查qApp->activeWindow(), qApp->allWidgets()等来监视应用程序的进度。

这几乎是唯一的方法。当然,您可以以任何您想要的方式注入代码,但是您需要使用一个与二进制兼容的Qt版本来编译代码。请注意,二进制兼容性不仅仅包括使用相同的编译器版本和Qt版本。许多configure开关也必须是相同的