views:

662

answers:

2

Hi guys, 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...

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).

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.

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.

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

    // 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.

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.

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.

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

+1  A: 

You might want to check out the EnumChildWindows function.

codebolt
A: 

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.

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.

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.

Alex Jenter