13

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

GetGUIThreadInfo API 看起来很有希望——它为另一个应用程序返回一个hwndFocus。但是我没有运气让它在我的 64 位操作系统上通过 C# 工作。我已经从pinvoke.net复制(然后进一步调整)声明,但我得到的只是一个通用错误代码(下面有更多详细信息)。

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

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

这是示例代码。我按照建议使用 GetWindowThreadProcessId ,所以我认为问题与混合线程 ID 和线程句柄无关:

[StructLayout(LayoutKind.Sequential)]
内部结构 Rect
{
    公共 int 左;
    公共int顶部;
    公共 int 权利;
    公共 int 底部;
}

[StructLayout(LayoutKind.Sequential)]
内部类 GuiThreadInfo
{
    公共 int cbSize;
    公共单位标志;
    公共 IntPtr hwndActive;
    公共 IntPtr hwndFocus;
    公共 IntPtr hwndCapture;
    公共 IntPtr hwndMenuOwner;
    公共 IntPtr hwndMoveSize;
    公共 IntPtr hwndCaret;
    公共矩形 rcCaret;
}

[DllImport("user32.dll")]
内部静态外部 uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("user32.dll", SetLastError = true)]
内部静态 extern bool GetGUIThreadInfo(uint idThread, ref GuiThreadInfo lpgui);

IntPtr GetFocusedHandleFromProcessWithWindow(IntPtr 窗口)
{
    var threadId = GetWindowThreadProcessId(window, IntPtr.Zero);
    var info = new GuiThreadInfo();
    info.cbSize = Marshal.SizeOf(info);
    if (!GetGUIThreadInfo(threadId, ref info))
        抛出新的 Win32Exception();
    返回 info.hwndFocus;
}

window是一个有效的窗口句柄;GetWindowThreadProcessId返回一个非零线程句柄。但是调用GetGUIThreadInfoalways 返回false,并且异常消息总是“参数不正确”。

以防万一问题是GetGUIThreadInfo不知何故没有 64 位版本,我尝试将声明IntPtr中的所有 8-byte s更改GuiThreadInfo为 4-byte ints,但我仍然遇到相同的错误。

有没有人GetGUIThreadInfo在 Win64 上有一个有效的 C# 示例?或者,是否有另一种方法可以在不激活该应用程序的情况下找到另一个应用程序中的焦点子窗口句柄?

4

1 回答 1

5

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

于 2009-04-06T00:42:25.190 回答