tags:

views:

640

answers:

1

I want to send keyboard input to a window in another process, without bringing that window to the foreground. I can use PostMessage to fake the WM_KEYDOWN and WM_KEYUP; all I need to know is which window handle should receive the keyboard input -- i.e., something like GetFocus, but for another, non-active application.

The GetGUIThreadInfo API looks promising -- it returns an hwndFocus for another app. But I've had no luck getting it to work from C# on my 64-bit OS. I've copied (and then further tweaked) the declarations from pinvoke.net, but all I ever get back is a generic error code (more details below).

I am setting cbSize before I call GetGUIThreadInfo, so I've avoided the most obvious potential problem.

I'm running 64-bit Vista, so I don't know whether the problem is that I'm not using the API correctly, or that it works differently in 64-bit -- I have yet to find a code sample that specifically says it works successfully in Win64.

Here's sample code. I'm using GetWindowThreadProcessId as recommended, so I don't think the problem has to do with mixing thread IDs with thread handles:

[StructLayout(LayoutKind.Sequential)]
internal struct Rect
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

[StructLayout(LayoutKind.Sequential)]
internal class GuiThreadInfo
{
    public int cbSize;
    public uint flags;
    public IntPtr hwndActive;
    public IntPtr hwndFocus;
    public IntPtr hwndCapture;
    public IntPtr hwndMenuOwner;
    public IntPtr hwndMoveSize;
    public IntPtr hwndCaret;
    public Rect rcCaret;
}

[DllImport("user32.dll")]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetGUIThreadInfo(uint idThread, ref GuiThreadInfo lpgui);

IntPtr GetFocusedHandleFromProcessWithWindow(IntPtr window)
{
    var threadId = GetWindowThreadProcessId(window, IntPtr.Zero);
    var info = new GuiThreadInfo();
    info.cbSize = Marshal.SizeOf(info);
    if (!GetGUIThreadInfo(threadId, ref info))
        throw new Win32Exception();
    return info.hwndFocus;
}

window is a valid window handle; GetWindowThreadProcessId returns a nonzero thread handle. But the call to GetGUIThreadInfo always returns false, and the exception message is always "The parameter is incorrect".

Just in case the problem was that GetGUIThreadInfo somehow doesn't have a 64-bit version, I tried changing all the 8-byte IntPtrs in the GuiThreadInfo declaration to 4-byte ints, but I still got the same error.

Does anyone have a working C# sample of GetGUIThreadInfo on Win64? Or, is there another way to find what the focused child-window handle would be in another app, without making that app active?

A: 

I haven't looked at it too closely but one thing jumps out. In your GetGUIThreadInfo call you pass the GUIThreadInfo structure by ref but you've defined it as a class so you are sending a reference by ref, in other words a pointer to a pointer. Either change your GUIThreadInfo to a struct or remove the ref on the parameter and add [In, Out] attributes.

Stephen Martin
Ouch. You're right -- I can't believe I missed that.Point of etiquette: this fixed the immediate problem, but then another problem cropped up; so my end goal of "get focused handle in other process" is still open. Should I edit this question, or accept this answer and then write a new question?
Joe White
In this case, the question is really about pInvoking GetGUIThreadInfo so you should probably vote it up as being helpful and mark it as the answer. You might also want to edit the title to more narrowly describe this question. Then add a new question about the more general problem including what...
Stephen Martin
...your underlying requirement is and what you've tried so far. In general, you should edit a question to add information or clarity not to change what is being asked.
Stephen Martin