通过P / Invoke调用GetGUIThreadInfo

时间:2009-04-05 22:40:31

标签: c# pinvoke

我想将键盘输入发送到另一个进程的窗口,而不将该窗口带到前台。我可以使用PostMessage伪造WM_KEYDOWNWM_KEYUP;我需要知道的是哪个窗口句柄应该接收键盘输入 - 即类似GetFocus,但是对于另一个非活动应用程序。

GetGUIThreadInfo API看起来很有前途 - 它会为另一个应用返回hwndFocus。但我没有运气在我的64位操作系统上使用C#工作。我已复制(然后进一步调整)来自pinvoke.net的声明,但我所得到的只是一般错误代码(详情如下)。

我在调用GetGUIThreadInfo之前设置cbSize,所以我避免了最明显的潜在问题。

我正在运行64位Vista,所以我不知道问题是我是不是正确使用了API,还是64位的工作方式不同 - 我还没找到代码专门说它在Win64中成功运行的示例。

这是示例代码。我正在使用GetWindowThreadProcessId as recommended,所以我不认为问题与将线程ID与线程句柄混合有关:

[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是一个有效的窗口句柄; GetWindowThreadProcessId返回非零线程句柄。但是对GetGUIThreadInfo的调用始终返回false,并且异常消息始终是“参数不正确”。

如果问题是GetGUIThreadInfo某种程度上没有64位版本,我尝试将IntPtr声明中的所有8字节GuiThreadInfo更改为4 -byte int s,但我仍然遇到同样的错误。

有没有人在Win64上有GetGUIThreadInfo的C#样本?或者,是否有另一种方法可以找到聚焦的子窗口句柄在另一个应用程序中的位置,而不会使该应用程序处于活动状态?

1 个答案:

答案 0 :(得分:5)

我没有仔细看过它,但有一件事情跳了出来。在您的GetGUIThreadInfo调用中,您通过ref传递GUIThreadInfo结构,但您已将其定义为类,因此您通过ref发送引用,换句话说,指向指针的指针。将GUIThreadInfo更改为结构或删除参数上的ref并添加[In,Out]属性。