模拟鼠标点击而不移动光标

4
我编写了一个应用程序,可以检测所有活动的 Windows 并将它们放入列表中。
是否有一种方法可以在不移动光标的情况下模拟点击与 Windows 位置相关的屏幕上的某个点?
我无法访问应该被点击的按钮句柄,只能访问窗口的句柄。

1
错误的问题。这里是你应该问的问题的答案:UI自动化。要进行实时演示,请查看随SDK一起提供的检查工具 - IInspectable
似乎很奇怪你想在一些随机窗口列表中按按钮。也许你正在寻找 SetForegroundWindow - Barmak Shemirani
@Barmak Shemirani,我有多个客户端窗口需要同时在屏幕上监视,程序应该将每个具有特定标题的窗口放入列表中,并在每个屏幕上以特定模式排列最多12个窗口。到目前为止,这已经可以实现了,我只需要一个额外的功能,即每n分钟点击第三方应用程序中的按钮以打开这些窗口。 - J. Grohmann
1个回答

7
有没有一种方式可以在不移动光标的情况下模拟相对于Windows位置屏幕上的某个点的鼠标单击?回答你的具体问题- 没有。 鼠标点击只能定向到鼠标光标实际存在的位置。模拟鼠标输入的正确方法是使用SendInput()(或在较旧的系统上使用mouse_event())。但是,这些函数将模拟的事件注入到与实际鼠标驱动程序发布到的相同输入队列中,因此它们将对鼠标光标产生物理影响- 即使将其移动到屏幕上等等。如何在没有SendInput的情况下模拟输入?

SendInput操作在输入栈的最底层。它只是键盘和鼠标驱动程序使用的相同输入机制的后门,用于告诉窗口管理器用户已经生成了输入。SendInput函数不知道输入会发生什么。这由窗口管理器的更高级别处理,例如组件,用于命中测试鼠标输入以查看消息最初应该传递到哪个窗口。

当将某物添加到队列中时,它需要时间才能从队列的前端出来。

当你调用SendInput时,你正在将输入数据包放入系统硬件输入队列中。(注意:这不是官方术语。今天我只是这样称呼它。)这是硬件设备驱动程序堆栈在物理设备报告事件时使用的相同输入队列。
消息进入硬件输入队列,原始输入线程会捡起它们。原始输入线程以高优先级运行,因此它很可能会非常快地捡起它们,但在多核机器上,您的代码可以继续运行,同时第二个核心运行原始输入线程。一旦出队事件,原始输入线程需要执行一些操作。如果有低级别的输入钩子,它必须调用每个钩子以查看它们是否想拒绝输入(而这些钩子可能需要很长时间才能决定)。只有在所有低级别钩子签署输入后,原始输入线程才被允许修改输入状态并导致GetAsyncKeyState报告按键已按下。
唯一真正的方法是找到位于所需屏幕坐标处的UI控件的HWND。然后你可以选择:
  1. 直接向其发送 WM_LBUTTONDOWNWM_LBUTTONUP 消息。或者,在标准的 Win32 按钮控件情况下,发送单个 BM_CLICK 消息。

  2. 使用 UI Automation API 的 AccessibleObjectFromWindow() 函数访问控件的 IAccessible 接口,然后调用其 accDoDefaultAction() 方法,对于按钮将点击它。

话虽如此,...

我无法访问应该被点击的按钮的句柄。

您可以访问任何具有 HWND 的内容。例如,看一下 WindowFromPoint()。您可以使用它来查找占用所需屏幕坐标的按钮的 HWND(当然,这带有一些警告:WindowFromPoint、ChildWindowFromPoint、RealChildWindowFromPoint,这将何时结束?)。


谢谢,我不知道获取按钮句柄这么简单,我会试一下的! - J. Grohmann
2
@ColdZer0:如果按钮控件有本机窗口支持,那将很容易。但通常情况下并非如此。Web浏览器通常不使用本机控件,Qt应用程序也是如此。尽管如此,这两者都支持UI自动化。 - IInspectable
1
@IInspectable:是的。如果在坐标处的实际HWND不是Win32按钮,则无法使用BM_CLICK。但是,您仍将拥有真正的HWND,并且将向其发送WM_LBUTTON...以在需要时向其持有的任何非窗口控件传递。但是,如果它支持UI自动化,那也很有帮助,因为您将能够枚举其不可访问的子控件。 - Remy Lebeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接