3

我有这个问题:我有一个特定应用程序的主窗口的处理程序,我想在那个应用程序上模拟一个按键......

我正在使用 sendMessage/postMessage api 调用来执行此操作。我不使用 .Net SendKeys 函数或 win32 api 的 keybd_event 的原因是它们在全局级别模拟按键。在我的情况下,目标应用程序不是最活跃的应用程序(其他应用程序可能运行在更高的 z 级别,因此覆盖了目标应用程序)。

sendMessage 和 postMessage 的问题在于,您必须传递要按下键的确切子窗口的处理程序。例如,在记事本中,如果我将密钥发送到主窗口的处理程序,什么也没有发生,我必须将密钥发送到子窗口的处理程序,该子窗口基本上由您可以编写的白色画布组成。

获取活动子窗口的处理程序是问题所在。一开始,我使用 GetTopWindow 或 GetWindow(GW_CHILD) api 调用,因为它返回最活跃的子窗口。我正在做的是继续调用 GetWindow(GW_CHILD),直到我得到一个没有更多 childWindows 的 childwindow。这适用于记事本或绘画等某些应用程序。但是,在某些情况下(例如 Firefox),它不起作用。原因是父窗口有整个firefox区域,而它的子窗口有打开的网页(如google)。所以,当我询问主窗口最活跃的子窗口时,它返回它拥有的唯一子窗口,也就是网页区域对应的那个。它仅在活动窗口是那个窗口时才有效(例如,如果用户在某个页面的文本框上写东西)。

我实际上找到了一种方法,使用 GetGUIThreadInfo api调用,使用以下代码:

    // get thread of the main window handle of the process
    var threadId = GetWindowThreadProcessId(firefox.MainWindowHandle, IntPtr.Zero);

    // get gui info
    var info = new GUITHREADINFO();
    info.cbSize = (uint)Marshal.SizeOf(info);
    if (!GetGUIThreadInfo(threadId, out info))
        throw new Win32Exception();

    // send the letter W to the active window
    PostMessage(info.hwndActive, WM_KEYDOWN, (IntPtr)Keys.W, IntPtr.Zero);

而且效果很好:如果地址栏处于活动状态,它会向地址栏发送一个“W”字母。如果谷歌的搜索文本框处于活动状态,它会向它发送“W”字母......完美!但是,我不能使用这种方法,原因很简单:如果目标应用程序不是操作系统的活动窗口,则 ThreadInfo 结构为空。例如,如果我的目标是 firefox,如果 firefox 处于活动状态(最顶层的应用程序,聚焦/活动的应用程序),它就可以工作,但是如果,比如说,记事本位于 firefox 之上,它就不起作用,它无法获取活动窗口处理程序。

我知道我可以通过使用 setForegroundWindow api 调用来激活目标应用程序然后捕获活动子窗口的处理程序来解决这个问题,但我不想将目标应用程序带到前台。

我还尝试了其他技术,例如 AttachThreadInput() 和 GetFocus() api 调用,它们也可以工作,但有同样的问题:如果目标应用程序不是活动的 Windows 应用程序,它就不起作用。

所以基本上我需要找到某种方法将处理程序获取到应用程序的活动子窗口,即使该应用程序不是最活跃的应用程序。

有任何想法吗?谢谢

4

2 回答 2

1

您可能想查看EnumChildWindows函数。

于 2009-05-11T14:09:32.903 回答
0

如果一切都失败了,这里有另一个想法:您可能要考虑使用 WH_CBT 或 WH_CALLWNDPROC 挂钩来监视目标线程的哪个子窗口最后被聚焦。

安装 CBT 挂钩 (WH_CBT) 并收听 HCBT_SETFOCUS 通知。或者使用 WH_CALLWNDPROC 钩子并监听 WM_SETFOCUS 消息。

不要在 hook proc 中做太多事情,否则你会占用系统资源。只需保存所需的信息并为自己发布一条自定义消息以供以后处理。

于 2009-05-14T05:21:51.497 回答