【问题标题】:Detect if on-screen keyboard is open (TabTip.exe)检测屏幕键盘是否打开 (TabTip.exe)
【发布时间】:2014-01-20 10:53:14
【问题描述】:

我正在开发一个用于填写表单的 WPF/C# 应用程序。我正在尝试找到一种方法来确定 TapTip 键盘(Windows 8 桌面的 TabTip.exe / Metro-like 键盘)是否在 Windows 8 中最小化/不可见。

我已经能够检测到 osk 键盘(osk.exe / windows 辅助功能屏幕键盘)是否被最小化,但相同的过程似乎不适用于 TabTip 键盘。

检测键盘是否最小化我:
1.查找键盘的进程
2.获取MainWindowHandle
3. 使用 WINDOWPLACEMENT 的 showCmd 属性(使用 MainWindowHandle 找到)
4.使用showCmd值判断窗口是否最小化

我遇到的问题是:
- TabTip 进程的 MainWindowHandle 为 0(所以我不能用它来查找 WINDOWPLACEMENT 信息)
- 当 TabTip 打开并最小化时,WINDOWPLACEMENT.showCmd 的值是相同的

为了找到 TabTip 窗口的句柄,我使用 ENUMWINDOWS 获取所有窗口句柄,使用 GETWINDOWTHREADPROCESSID 获取进程 id,然后将 id 与 TabTip 进程 id 进行比较。

对此的任何帮助将不胜感激。这也是我的第一篇文章。我认为我做对了,但如果不是,请告诉我如何解决。

【问题讨论】:

    标签: c# windows-8 keyboard touch on-screen-keyboard


    【解决方案1】:

    在找到一种可行的方法之前,我尝试了几种不同的方法。使用IsWindowVisible() 不起作用,我也对GetWindowPlacement()GetIconic() 不满意。最后我使用了GetWindowLong() 并检查了WS_VISIBLE 是否回来了。一个快速的控制台应用演示如下:

    using System;
    using System.Diagnostics;
    using Microsoft.Win32;
    using System.Runtime.InteropServices;
    using System.Threading;
    
    namespace CSharpTesting
    {
        class Program
        {
            /// <summary>
            /// The window is initially visible. See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms632600(v=vs.85).aspx.
            /// </summary>
            public const UInt32 WS_VISIBLE  = 0X94000000;
            /// <summary>
            /// Specifies we wish to retrieve window styles.
            /// </summary>
            public const int GWL_STYLE = -16;
    
            [DllImport("user32.dll")]
            public static extern IntPtr FindWindow(String sClassName, String sAppName);
    
            [DllImport("user32.dll", SetLastError = true)]
            static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);
    
            static void Main(string[] args)
            {
                // Crappy loop to poll window state.
                while (true)
                {
                    if (IsKeyboardVisible())
                    {
                        Console.WriteLine("keyboard is visible");
                    }
                    else
                    {
                        Console.WriteLine("keyboard is NOT visible");
                    }
    
                    Thread.Sleep(1000);
                }
            }
    
            /// <summary>
            /// Gets the window handler for the virtual keyboard.
            /// </summary>
            /// <returns>The handle.</returns>
            public static IntPtr GetKeyboardWindowHandle()
            {
                return FindWindow("IPTip_Main_Window", null);
            }
    
            /// <summary>
            /// Checks to see if the virtual keyboard is visible.
            /// </summary>
            /// <returns>True if visible.</returns>
            public static bool IsKeyboardVisible()
            {
                IntPtr keyboardHandle = GetKeyboardWindowHandle();
    
                bool visible = false;
    
                if (keyboardHandle != IntPtr.Zero)
                {
                    UInt32 style = GetWindowLong(keyboardHandle, GWL_STYLE);
                    visible = (style == WS_VISIBLE);
                }
    
                return visible;
            }
        }
    }
    

    【讨论】:

    • 如果屏幕键盘与光标所在的屏幕不同,这似乎不起作用。我在 Windows 10 机器上。
    • 用户第一次登录windows后就不行了。它报告键盘在不可见时可见。但是,如果用户手动打开键盘然后关闭它,它会开始正确报告。有没有人可以解决这个问题?
    • 对我来说,这就是登录后和其他时间一直有效的方法:UInt32 WS_VISIBLE = 0X94000000; ... 可见 = (style == WS_VISIBLE);
    • style 是一个长型位模式,用于保存 N 个样式设置标志,因此如果您想知道是否设置了 WS_VISIBLE 标志,请使用 & 位运算符:“style & WS_VISIBLE”
    • 在您的消息中您说您检查WS_DISABLED,但在您的代码中您实际上检查WS_VISIBLE
    【解决方案2】:

    这完全有效!

    //
    // BOOL IsVirtualKeyboardVisible()
    //
    // Returns TRUE if Virtual Keyboard/Input Pane is visible
    // Returns FALSE if Virtual Keyboard/Input Pane is not visible
    
    __declspec(dllexport) BOOL __cdecl IsVirtualKeyboardVisible()
    {
        BOOL    bRet = FALSE;
        RECT    InputPaneScreenLocation = { 0, 0, 0, 0 };
    
        __try
        {
            HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
    
            IFrameworkInputPane *IinputPane = NULL;
    
            if (SUCCEEDED(hr))
            {
                //
                // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706967(v=vs.85).aspx
                //
                hr = CoCreateInstance(__uuidof(FrameworkInputPane), 0, CLSCTX_ALL, __uuidof(IFrameworkInputPane), (LPVOID*)&IinputPane);
                IinputPane->Location(&InputPaneScreenLocation);
    
                if (InputPaneScreenLocation.bottom == 0 && InputPaneScreenLocation.left == 0 &&
                    InputPaneScreenLocation.right == 0 && InputPaneScreenLocation.top == 0)
                {
                    // VKB is not visible
                    bRet = FALSE;
                }
                else
                {
                    // VKB is visible
                    bRet = TRUE;
                } 
            }
    
        }   // try
        __finally
        {
            CoUninitialize();
        }
    
        return bRet;
    }
    

    【讨论】:

    • ... 为什么会这样?请解释为什么此代码有效,因为这不鼓励在没有完全理解的情况下进行复制和粘贴。 OP 应该能够理解您在设计此代码时选择背后的后果,并确定此代码是否适合解决任务。
    • 此解决方案在 v1909 上始终适用于我。它使用代码中链接的 IFrameworkInputPane 接口,该接口专门定位触摸键盘和 IME 使用的输入窗格。
    【解决方案3】:

    如果我没记错的话,TabTip.exe 的窗口类名称是 IPTip_Main_Window。您可以使用 Win32 API FindWindow 来获取 TabTip.exeHWND。这比使用窗口标题更可靠,建议使用,因为某些窗口可以有空标题(或标题可以更改)。

    您当前使用EnumWindows 的方法可能存在缺陷,因为单个进程具有许多窗口(或具有子窗口的窗口)。您可以使用Spy++ 之类的工具来查找您想要的实际窗口和相应的类名。

    此时您仍然可以使用GetWindowHandleThreadProcessId 检索processID,但我认为您不需要它来进行简单的窗口状态监控。

    另外,请尝试使用 Win32 API,而不是 CLR 中内置的任何内容。例如GetWindowPlacement

    来自 MSDN 的说明:

    此函数检索到的 WINDOWPLACEMENT 的标志成员是 始终为零。如果 hWnd 参数标识的窗口是 最大化时,showCmd 成员为 SW_SHOWMAXIMIZED。如果窗口是 最小化,showCmd 是 SW_SHOWMINIMIZED。否则就是 SW_SHOWNORMAL。

    希望对您有所帮助,如果您仍需要进一步的帮助,请发表评论,我会在回到我的 Win8 机器后进行编辑。

    【讨论】:

    • 感谢您的快速回复。我像你建议的那样改用 FindWindow 。然后我使用 Spy++ 来验证是否找到了正确的窗口。不幸的是,当使用 GetWindowPlacement 时,当键盘的进程被杀死时,showCmd 的值只有 0 (SW_HIDE)。其余时间 showCmd 的值为 1 (SW_NORMAL),无论它是否可见。我还尝试使用 GetWindowRect 来查看窗口是否被“隐藏”在屏幕外。当不可见时,返回的坐标与它在屏幕上的最后可见位置相同。
    • 你有没有想过这个问题?这两种方法我都试过了,似乎都不管用。我认为 TabTip.exe 是一个与服务相结合的特殊进程,因此检测 hwnd 是否最小化/关闭的标准方法似乎不起作用。
    • 我一直没有机会进一步研究它,抱歉!
    • 好吧,我可以确认您可以使用 FindWindow 来确定 TabTip 是否已在运行。但如果它正在运行并隐藏,我就是不知道如何告诉它再次显示窗口。我已经尝试将 ShowWindow 与 SW_SHOW 一起使用,但那里没有运气......有一个丑陋的解决方法,将 SC_CLOSE 发送到该窗口并再次启动 TabTip。不好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 2011-09-01
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多