通过 P/Invoke 调用 GetGUIThreadInfo

发布于 2024-07-17 00:07:17 字数 2416 浏览 8 评论 0原文

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

GetGUIThreadInfo API 看起来很有前途——它返回一个hwndFocus 用于另一个应用程序。 但我没能成功地在我的 64 位操作系统上使用 C# 运行它。 我已经复制(然后进一步调整)来自 pinvoke.net 的声明,但我得到的只是一个一般错误代码(更多详细信息如下)。

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

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

这是示例代码。 我正在使用 GetWindowThreadProcessId 按照建议,所以我不要认为问题与混合线程 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 位版本,我尝试更改 GuiThreadInfo< 中的所有 8 字节 IntPtr /code> 声明为 4 字节 int ,但我仍然遇到相同的错误。

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

虐人心 2024-07-24 00:07:17

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文