确定Windows 10 Touch键盘是可见还是隐藏

我试图找出,如果Windows 10的虚拟触摸键盘是可见的或不知道是否打开或不从我的应用程序。 下面的代码工作正常,直到最新的Windows 10更新15063或可能的前一个。 似乎微软可能改变了窗口样式的东西,但我无法弄清楚。

public static bool IsKeyboardVisible() { IntPtr keyboardHandle = GetKeyboardWindowHandle(); // Specifies we wish to retrieve window styles. int GWL_STYLE = -16; //The window is disabled. See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms632600(v=vs.85).aspx. UInt32 WS_VISIBLE = 0x10000000; UInt32 WS_DISABLED = 0x08000000; UInt32 WS_POPUP = 0x80000000; bool visible = false; bool disabled = false; if (keyboardHandle != IntPtr.Zero) { UInt32 style = GetWindowLong(keyboardHandle, GWL_STYLE); visible = ((style & WS_VISIBLE) == WS_VISIBLE); disabled = ((style & WS_DISABLED) == WS_DISABLED); // ref https://stackoverflow.com/questions/11065026/get-window-state-of-another-process log.InfoFormat("style:{0:X4} visible:{1} disabled:{2}", style, visible, disabled); } return visible && !disabled ; } 

这与Windows 10周年纪念版中的显示触摸键盘(TabTip.exe)有关

我用Spy ++做了一些研究。 看起来像Fall Creators Update(1709年版本)中的新键盘是由另一个窗口托管的。 该窗口具有Windows.UI.Core.CoreWindow类和Microsoft Text Input Application作为其标题。

当键盘可见时,此窗口是具有Windows.UI.Core.CoreWindow类的另一个窗口的子项。 如果键盘被隐藏 – 窗口变成没有父母的顶级窗口。

下面的代码适用于1709和更旧的Windows版本(我相信从Windows 8开始)。

 static class TouchKeyboard { public static bool GetIsOpen() { return GetIsOpen1709() ?? GetIsOpenLegacy(); } private static bool? GetIsOpen1709() { // if there is a top-level window - the keyboard is closed var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass1709, WindowCaption1709); if (wnd != IntPtr.Zero) return false; var parent = IntPtr.Zero; for (;;) { parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709); if (parent == IntPtr.Zero) return null; // no more windows, keyboard state is unknown // if it's a child of a WindowParentClass1709 window - the keyboard is open wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709); if (wnd != IntPtr.Zero) return true; } } private static bool GetIsOpenLegacy() { var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass); if (wnd == IntPtr.Zero) return false; var style = GetWindowStyle(wnd); return style.HasFlag(WindowStyle.Visible) && !style.HasFlag(WindowStyle.Disabled); } private const string WindowClass = "IPTip_Main_Window"; private const string WindowParentClass1709 = "ApplicationFrameWindow"; private const string WindowClass1709 = "Windows.UI.Core.CoreWindow"; private const string WindowCaption1709 = "Microsoft Text Input Application"; private enum WindowStyle : uint { Disabled = 0x08000000, Visible = 0x10000000, } private static WindowStyle GetWindowStyle(IntPtr wnd) { return (WindowStyle)GetWindowLong(wnd, -16); } [DllImport("user32.dll", SetLastError = false)] private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null); [DllImport("user32.dll", SetLastError = false)] private static extern uint GetWindowLong(IntPtr wnd, int index); } 

我发现了另一个未公开的COM API,它返回触摸键盘的位置。 它返回键盘窗口的边界或零,如果键盘被隐藏。 我在Windows 8.1Windows 10Windows 10 Fall Creators Update测试它,它工作正常。

现在有一个坏消息:在Fall Creators Update之前的所有版本中,只有当活动窗口和触摸键盘位于同一显示器上时,才会报告准确的结果。 如果不是这种情况 – API只是返回先前的缓存值。 我猜这跟这个API本来就是用来计算触摸键盘和你的应用程序的窗口的遮挡有关。 (它在Windows::UI::ViewManagement::InputPane.get_OccludedRect() UWP API中调用)。

所以如果你不关心支持旧版本或多监视器的情况 – 使用它。 否则,我会建议检查Windows版本,并回到以前的方法( GetIsOpenLegacy()从我的其他答案)。

API:

 [ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")] class ImmersiveShellBroker { } [ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IImmersiveShellBroker { void Dummy(); IInputHostManagerBroker GetInputHostManagerBroker(); } [ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IInputHostManagerBroker { void GetIhmLocation(out Rect rect, out DisplayMode mode); } [StructLayout(LayoutKind.Sequential)] struct Rect { public int Left, Top, Right, Bottom; } enum DisplayMode { NotSupported = 0, Floating = 2, Docked = 3, } 

用法示例:

 // do this once: var brokerClass = new ImmersiveShellBroker(); var broker = (IImmersiveShellBroker)brokerClass; var ihm = broker.GetInputHostManagerBroker(); Marshal.ReleaseComObject(broker); // now ihm reference can be cached and used later: Rect rect; DisplayMode mode; ihm.GetIhmLocation(out rect, out mode); 

注意:看起来像GetIhmLocation()总是返回DisplayMode.NotSupported而不是Windows 10之前的实际mode