Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if Windows 10 Touch Keyboard is Visible or Hidden

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 up until the latest Windows 10 update 15063 or possible the one right before it. Seems like Microsoft changed something with the window styles possibly but I can't figure it out.

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

This is related to: Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition

like image 214
mikesl Avatar asked Jan 29 '23 06:01

mikesl


2 Answers

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.

like image 113
torvin Avatar answered Feb 02 '23 10:02

torvin


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://learn.microsoft.com/en-us/windows/desktop/api/shobjidl_core/nn-shobjidl_core-iframeworkinputpane)

like image 20
Andrea S. Avatar answered Feb 02 '23 11:02

Andrea S.