确定 Windows 10 触摸键盘是可见还是隐藏
Determine if Windows 10 Touch Keyboard is Visible or Hidden
我正在尝试确定 windows 10 虚拟触摸键盘是否可见,以了解是否从我的应用程序中打开它。以下代码一直运行良好,直到最近的 Windows 10 更新 15063 或可能是它之前的那个。微软似乎用 window 样式改变了一些东西,但我想不通。
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
log.InfoFormat("style:{0:X4} visible:{1} disabled:{2}", style, visible, disabled);
}
return visible && !disabled ;
}
这与:Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition
相关
我对 Spy++ 做了一些研究。看起来 Fall Creators Update(版本 1709)中的新键盘由另一个 window 托管。此 window 的标题为 Windows.UI.Core.CoreWindow
class 和 Microsoft Text Input Application
。
以下代码适用于所有 Windows 10 版本,包括新的 1803 和旧的 Windows 版本(我相信从 Windows 8 开始)。
static class TouchKeyboard
{
public static bool GetIsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
private static bool? GetIsOpen1709()
{
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
var 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);
}
更新: 我更新了答案和代码以与 Redstone 4 (v1803) 兼容。
我发现了另一个未记录的 COM API,returns 触摸键盘的位置。它 returns 键盘的边界 window 或如果键盘被隐藏则归零。我在 Windows 8.1
、Windows 10
和 Windows 10 Fall Creators Update
中对其进行了测试,并且工作正常。
现在有一些坏消息:在 Fall Creators Update
之前的所有版本中,如果活动 window 和触摸键盘位于同一台显示器上,它只会报告准确的结果。如果不是这种情况 - API 只是 returns 之前的缓存值。我猜这与此 API 旨在用于计算触摸键盘和您应用程序的 window 的遮挡这一事实有关。 (它在 Windows::UI::ViewManagement::InputPane.get_OccludedRect()
UWP API 内部调用)。
因此,如果您不关心支持旧版本或多显示器方案,请使用它。否则我会建议检查 Windows 版本并回退到以前的方法( 来自我的其他答案)。
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()
总是 returns DisplayMode.NotSupported
而不是 Windows 之前的实际 mode
10.
我正在使用这个解决方案,它正在 Windows 1607、1709 和 1803 上工作(检查下面代码中的 Main 方法):
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[ComImport, Guid("D5120AA3-46BA-44C5-822D-CA8092C1FC72")]
public class FrameworkInputPane
{
}
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("5752238B-24F0-495A-82F1-2FD593056796")]
public interface IFrameworkInputPane
{
[PreserveSig]
int Advise(
[MarshalAs(UnmanagedType.IUnknown)] object pWindow,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int AdviseWithHWND(
IntPtr hwnd,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int Unadvise(
int pdwCookie
);
[PreserveSig]
int Location(
out Rectangle prcInputPaneScreenLocation
);
}
static void Main(string[] args)
{
var inputPane = (IFrameworkInputPane)new FrameworkInputPane();
inputPane.Location(out var rect);
Console.WriteLine((rect.Width == 0 && rect.Height == 0) ? "Keyboard not visible" : "Keyboard visible");
}
}
}
它使用 IFrameworkInputPane 接口 (https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane)
我正在尝试确定 windows 10 虚拟触摸键盘是否可见,以了解是否从我的应用程序中打开它。以下代码一直运行良好,直到最近的 Windows 10 更新 15063 或可能是它之前的那个。微软似乎用 window 样式改变了一些东西,但我想不通。
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
log.InfoFormat("style:{0:X4} visible:{1} disabled:{2}", style, visible, disabled);
}
return visible && !disabled ;
}
这与:Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition
相关我对 Spy++ 做了一些研究。看起来 Fall Creators Update(版本 1709)中的新键盘由另一个 window 托管。此 window 的标题为 Windows.UI.Core.CoreWindow
class 和 Microsoft Text Input Application
。
以下代码适用于所有 Windows 10 版本,包括新的 1803 和旧的 Windows 版本(我相信从 Windows 8 开始)。
static class TouchKeyboard
{
public static bool GetIsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
private static bool? GetIsOpen1709()
{
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
var 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);
}
更新: 我更新了答案和代码以与 Redstone 4 (v1803) 兼容。
我发现了另一个未记录的 COM API,returns 触摸键盘的位置。它 returns 键盘的边界 window 或如果键盘被隐藏则归零。我在 Windows 8.1
、Windows 10
和 Windows 10 Fall Creators Update
中对其进行了测试,并且工作正常。
现在有一些坏消息:在 Fall Creators Update
之前的所有版本中,如果活动 window 和触摸键盘位于同一台显示器上,它只会报告准确的结果。如果不是这种情况 - API 只是 returns 之前的缓存值。我猜这与此 API 旨在用于计算触摸键盘和您应用程序的 window 的遮挡这一事实有关。 (它在 Windows::UI::ViewManagement::InputPane.get_OccludedRect()
UWP API 内部调用)。
因此,如果您不关心支持旧版本或多显示器方案,请使用它。否则我会建议检查 Windows 版本并回退到以前的方法(
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()
总是 returns DisplayMode.NotSupported
而不是 Windows 之前的实际 mode
10.
我正在使用这个解决方案,它正在 Windows 1607、1709 和 1803 上工作(检查下面代码中的 Main 方法):
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[ComImport, Guid("D5120AA3-46BA-44C5-822D-CA8092C1FC72")]
public class FrameworkInputPane
{
}
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("5752238B-24F0-495A-82F1-2FD593056796")]
public interface IFrameworkInputPane
{
[PreserveSig]
int Advise(
[MarshalAs(UnmanagedType.IUnknown)] object pWindow,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int AdviseWithHWND(
IntPtr hwnd,
[MarshalAs(UnmanagedType.IUnknown)] object pHandler,
out int pdwCookie
);
[PreserveSig]
int Unadvise(
int pdwCookie
);
[PreserveSig]
int Location(
out Rectangle prcInputPaneScreenLocation
);
}
static void Main(string[] args)
{
var inputPane = (IFrameworkInputPane)new FrameworkInputPane();
inputPane.Location(out var rect);
Console.WriteLine((rect.Width == 0 && rect.Height == 0) ? "Keyboard not visible" : "Keyboard visible");
}
}
}
它使用 IFrameworkInputPane 接口 (https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane)