Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling GetGUIThreadInfo via P/Invoke

Tags:

c#

pinvoke

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?

like image 312
Joe White Avatar asked Apr 05 '09 22:04

Joe White


1 Answers

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.

like image 153
Stephen Martin Avatar answered Oct 31 '22 11:10

Stephen Martin