Home » Windows » Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition

Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition

Posted by: admin December 21, 2017 Leave a comment

Questions:

In Windows 8 and Windows 10 before Anniversary update it was possible to show touch keyboard by starting

C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe

It no longer works in Windows 10 Anniversary update; the TabTip.exe process is running, but the keyboard is not shown.

Is there a way to show it programmatically?

UPDATE

I found a workaround – fake mouse click on touch keyboard icon in system tray. Here is code in Delphi

// Find tray icon window
function FindTrayButtonWindow: THandle;
var
  ShellTrayWnd: THandle;
  TrayNotifyWnd: THandle;
begin
  Result := 0;
  ShellTrayWnd := FindWindow('Shell_TrayWnd', nil);
  if ShellTrayWnd > 0 then
  begin
    TrayNotifyWnd := FindWindowEx(ShellTrayWnd, 0, 'TrayNotifyWnd', nil);
    if TrayNotifyWnd > 0 then
    begin
      Result := FindWindowEx(TrayNotifyWnd, 0, 'TIPBand', nil);
    end;
  end;
end;

// Post mouse click messages to it
TrayButtonWindow := FindTrayButtonWindow;
if TrayButtonWindow > 0 then
begin
  PostMessage(TrayButtonWindow, WM_LBUTTONDOWN, MK_LBUTTON, $00010001);
  PostMessage(TrayButtonWindow, WM_LBUTTONUP, 0, $00010001);
end;

UPDATE 2

Another thing I found is that setting this registry key restores old functionality when starting TabTip.exe shows touch keyboard

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TabletTip.7\EnableDesktopModeAutoInvoke=1
Answers:

OK, I reverse engineered what explorer does when the user presses that button in the system tray.

Basically it creates an instance of an undocumented interface ITipInvocation and calls its Toggle(HWND) method, passing desktop window as an argument. As the name suggests, the method either shows or hides the keyboard depending on its current state.

Please note that explorer creates an instance of ITipInvocation on every button click. So I believe the instance should not be cached. I also noticed that explorer never calls Release() on the obtained instance. I’m not too familiar with COM, but this looks like a bug.

I tested this in Windows 8.1, Windows 10 & Windows 10 Anniversary Edition and it works perfectly. Here’s a minimal example in C that obviously lacks some error checks.

#include <initguid.h>
#include <Objbase.h>
#pragma hdrstop

// 4ce576fa-83dc-4F88-951c-9d0782b4e376
DEFINE_GUID(CLSID_UIHostNoLaunch,
    0x4CE576FA, 0x83DC, 0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76);

// 37c994e7_432b_4834_a2f7_dce1f13b834b
DEFINE_GUID(IID_ITipInvocation,
    0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b);

struct ITipInvocation : IUnknown
{
    virtual HRESULT STDMETHODCALLTYPE Toggle(HWND wnd) = 0;
};

int WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    HRESULT hr;
    hr = CoInitialize(0);

    ITipInvocation* tip;
    hr = CoCreateInstance(CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER, IID_ITipInvocation, (void**)&tip);
    tip->Toggle(GetDesktopWindow());
    tip->Release();
    return 0;
}

Here’s the C# version as well:

class Program
{
    static void Main(string[] args)
    {
        var uiHostNoLaunch = new UIHostNoLaunch();
        var tipInvocation = (ITipInvocation)uiHostNoLaunch;
        tipInvocation.Toggle(GetDesktopWindow());
        Marshal.ReleaseComObject(uiHostNoLaunch);
    }

    [ComImport, Guid("4ce576fa-83dc-4F88-951c-9d0782b4e376")]
    class UIHostNoLaunch
    {
    }

    [ComImport, Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface ITipInvocation
    {
        void Toggle(IntPtr hwnd);
    }

    [DllImport("user32.dll", SetLastError = false)]
    static extern IntPtr GetDesktopWindow();
}

Update: per @EugeneK comments, I believe that tabtip.exe is the COM server for the COM component in question, so if your code gets REGDB_E_CLASSNOTREG, it should probably run tabtip.exe and try again.

Questions:
Answers:

The only solution I’ve found to work is by sending PostMessage as you’ve mentioned in answer 1. Here’s the C# version of it in case someone needs it.

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr FindWindow(string sClassName, string sAppName);

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
    static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle); 

[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
    static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);

var trayWnd = FindWindow("Shell_TrayWnd", null);
var nullIntPtr = new IntPtr(0);

if (trayWnd != nullIntPtr)
{
    var trayNotifyWnd = FindWindowEx(trayWnd, nullIntPtr, "TrayNotifyWnd", null);
    if (trayNotifyWnd != nullIntPtr)
    {
        var tIPBandWnd = FindWindowEx(trayNotifyWnd, nullIntPtr, "TIPBand", null);

        if (tIPBandWnd != nullIntPtr)
        {
            PostMessage(tIPBandWnd, (UInt32)WMessages.WM_LBUTTONDOWN, 1, 65537);
            PostMessage(tIPBandWnd, (UInt32)WMessages.WM_LBUTTONUP, 1, 65537);
        }
    }
}


public enum WMessages : int
{
    WM_LBUTTONDOWN = 0x201,
    WM_LBUTTONUP = 0x202,
    WM_KEYDOWN = 0x100,
    WM_KEYUP = 0x101,
    WH_KEYBOARD_LL = 13,
    WH_MOUSE_LL = 14,
}

Questions:
Answers:

I detect 4 situations when trying to open Touch Keyboard on Windows 10 Anniversary Update

  1. Keyboard is Visible – when “IPTIP_Main_Window” is present, NOT disabled and IS visible
  2. Keyboard is not visible – when “IPTIP_Main_Window” is present but disabled
  3. Keyboard is not visible – when “IPTIP_Main_Window” is present but NOT disabled and NOT visible
  4. Keyboard is not visible – when “IPTIP_Main_Window” is NOT present

1 – nothing to do

2+3 – activating via COM

4 – most interesting scenario. In some devices starting TabTip process opens touch keyboard, on some – not. So we must start TabTip process, wait for appearing window “IPTIP_Main_Window”, check it for visibility and activate it via COM if nessesary.

I make small library for my project, you can use it – osklib

Questions:
Answers:

The problem seems to be with setting of Windows OS. I have faced same issue with the app I was developing. With Windows 8 and 10 (before update) code that called keyboard worked fine, but after update failed to work. After reading this article, I did following:

  1. Pressed Win+I to open the Settings app

  2. Clicked on Devices > Typing

  3. Turned “Automatically show the touch keyboard in windowed apps when there’s no keyboard attached to your device” ON.

    Right after that keyboard starting showing up in Windows 10 also.

Questions:
Answers:

There is still some mystery about how the touch keyboard is set visible by Windows 10 Anniversary Update. I’m actually having the exact same issue and here are the lastest infos i’ve found :

  • Windows 10 1607 works in two modes : Desktop and Tablet. While in Desktop mode, TabTip.exe can be called but won’t show. While in Tablet mode, everything works fine : TabTip.exe shows itself when called. So a 100% working workaround is to set your computer in Tablet Mode but who wants his desktop/laptop to work in tablet mode ? Not me anyway !

  • You can use the “EnableDesktopModeAutoInvoke” Key (HKCU, DWORD set to 1) and on some computers running 1607 it worked great while in Desktop Mode. But for some unknown reasons, it is not working on my HP touchpad.

Please note that this registry value is the “Show touch keyboard on desktop mode if there is no attached keyboard” option in Windows parameters > touch

  • You can use Torvin’s code to show TabTip.exe (as mentioned TabTip.exe should be running when you do the COM stuff), it is working fine on some computers running 1607 (including my HP touchpad ! yay !) But it will do nothing on some others comps with the same windows Build.

So far tested on 4 different computers and i’m unable to get something working fine on all…

Questions:
Answers:

Implementing the IValueProvider/ITextProvider in your control is a correct way to achieve this, as described here: https://stackoverflow.com/a/43886052/1184950