如何获取应用程序的活动ChildWindow?

时间:2022-06-29 21:22:12

I have this problem: I have an handler to the mainWindow of a certain application, and I want to simulate a keypress on that application...

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

I'm using sendMessage/postMessage api calls to do this. The reason why I don't use the .Net SendKeys function or the keybd_event of the win32 api, is that they simulate the keypress at a global level. In my case, the target application is not the top-active one (other application may be running in a higher z-level, hence covering the target app).

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

The problem with sendMessage and postMessage is that you must pass the handler of the exact childwindow where you want the key to be pressed. For example, in notepad, if I send the key to the handler of the mainWindow, nothing happens, I have to send the key to the handler of the child window that basically consists of the white canvas where you can write.

sendMessage和postMessage的问题在于您必须传递精确子窗口的处理程序,您希望按下该键。例如,在记事本中,如果我将密钥发送到mainWindow的处理程序,则没有任何反应,我必须将密钥发送到子窗口的处理程序,该处理程序基本上由可以编写的白色画布组成。

Obtaining the handler to the active child window is the problem. In the beginning, I was using the GetTopWindow or GetWindow(GW_CHILD) api calls, as it returns the most active child window. What I was doing was to keep calling the GetWindow(GW_CHILD) until I got a childwindow that had no more childWindows. This works ok for some applications like notepad or paint. However, in some cases (like firefox for example), it doesn't work. The reason for that is that the parent window has the whole firefox area, and its childwindow has the opened WebPage (like google). So, when I ask for the most active child window of the mainWindow, it returns the only child window it has, which is the one corresponding to the Webpage area. It only works if the active window is that one (like if the user is writing something on a textbox of a certain page). But if what is active is, let's say, the address bar, it doesn't work because the active window is not the child window but actually the parent... and I can't get this information programatically.

获取活动子窗口的处理程序是问题所在。在开始时,我使用GetTopWindow或GetWindow(GW_CHILD)api调用,因为它返回最活跃的子窗口。我正在做的是继续调用GetWindow(GW_CHILD),直到我得到一个没有childWindows的子窗口。这适用于记事本或油漆等应用。但是,在某些情况下(例如firefox),它不起作用。原因是父窗口具有整个firefox区域,其子窗口具有打开的WebPage(如google)。因此,当我要求mainWindow最活跃的子窗口时,它返回它所拥有的唯一子窗口,这是与网页区域对应的窗口。它仅在活动窗口是那个窗口时才有效(就像用户在某个页面的文本框上写东西一样)。但是,如果活动的是,地址栏,它不起作用,因为活动窗口不是子窗口,但实际上是父窗口......我无法以编程方式获取此信息。

I actually found a way of doing this, using the GetGUIThreadInfo api call, using the following code:

我实际上使用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);

And it works very well: If the address bar is active, it sends a "W" letter to the address bar. If the search textBox of google is active, it sends the "W" letter to it... Perfect! However, this method can't be used by me for a simple reason: If the target application is not the active window of the operating system, the ThreadInfo structure comes empty. For example, if I'm targetting firefox, it works if firefox is active (the top-most application, the focused/active one), but if, let's say, notepad is on top of firefox, it doesn't work, it is unable to get the active window handler.

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

I know I can solve this by using the setForegroundWindow api call to activate the target application and then capture the handler of the active child-window but I didn't want to have to bring the target app to the foreground.

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

I've also tried other techniques like AttachThreadInput() and GetFocus() api calls, which also works, but has the same problem: If the target application is not the active windows application, it doesn't work.

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

So basically I need to find some way of getting the handler to the active childwindow of an application even if that application is not the top-active one.

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

Any ideas? Thanks

有任何想法吗?谢谢

2 个解决方案

#1


You might want to check out the EnumChildWindows function.

您可能想要查看EnumChildWindows函数。

#2


If everything else fails, here is another idea: You might want to consider using a WH_CBT or a WH_CALLWNDPROC hooks to monitor which child window of the target thread has been focused last.

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

Install a CBT hook (WH_CBT) and listen for the HCBT_SETFOCUS notification. Or use a WH_CALLWNDPROC hook and listen for the WM_SETFOCUS message.

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

Don't do much in the hook proc or you'll hog down the system resources. Just save the needed information and post yourself a custom message to process it later.

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

#1


You might want to check out the EnumChildWindows function.

您可能想要查看EnumChildWindows函数。

#2


If everything else fails, here is another idea: You might want to consider using a WH_CBT or a WH_CALLWNDPROC hooks to monitor which child window of the target thread has been focused last.

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

Install a CBT hook (WH_CBT) and listen for the HCBT_SETFOCUS notification. Or use a WH_CALLWNDPROC hook and listen for the WM_SETFOCUS message.

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

Don't do much in the hook proc or you'll hog down the system resources. Just save the needed information and post yourself a custom message to process it later.

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