Determine if Windows 10 Touch Keyboard is Visible or Hidden

前端 未结 3 529
后悔当初
后悔当初 2020-12-06 14:22

I am trying to find out if the windows 10 virtual touch keyboard is visible or not to know whether to open it or not from my application. THe following code has worked fine

相关标签:
3条回答
  • 2020-12-06 14:50

    I'm using this solution, and it is working on Windows 1607, 1709 and 1803 (check the Main method below on the code):

    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");
            }
        }
    }
    

    It uses the IFrameworkInputPane interface (https://docs.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane)

    0 讨论(0)
  • 2020-12-06 15:00

    I've done some research with Spy++ . Looks like the new keyboard in Fall Creators Update (ver. 1709) is hosted by another window. This window has Windows.UI.Core.CoreWindow class and Microsoft Text Input Application as its title.

    The following code works for all Windows 10 versions including the new 1803 and older Windows versions as well (starting with Windows 8, I believe).

    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);
    }
    

    Update: I updated the answer and the code to be compatible with Redstone 4 (v1803) as well.

    0 讨论(0)
  • 2020-12-06 15:05

    I discovered yet another undocumented COM API that returns the position of the touch keyboard. It returns the bounds of the keyboard window or zeroes if the keyboard is hidden. I tested it in Windows 8.1, Windows 10 and Windows 10 Fall Creators Update and it works fine.

    Now some bad news: in all versions prior to Fall Creators Update it only reports accurate results if the active window and the touch keyboard are located on the same monitor. If this is not the case - the API just returns the previous cached value. I'm guessing it has something to do with the fact that this API was meant to be used to calculate occlusion of the touch keyboard and your app's window. (It's called inside Windows::UI::ViewManagement::InputPane.get_OccludedRect() UWP API).

    So if you don't care about supporting older versions or multi-monitor scenarios - use it. Otherwise I would suggest checking the Windows version and falling back to the previous method (GetIsOpenLegacy() from my other answer).

    The 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,
    }
    

    Usage example:

    // 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);
    

    Note: looks like GetIhmLocation() always returns DisplayMode.NotSupported instead of the actual mode prior to Windows 10.

    0 讨论(0)
提交回复
热议问题