Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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\1.7\EnableDesktopModeAutoInvoke=1 
like image 452
EugeneK Avatar asked Aug 04 '16 17:08

EugeneK


People also ask

Where is TabTip exe located?

TabTip.exe file information TabTip.exe is located in a subfolder of "C:\Program Files\Common Files" or sometimes in a subfolder of "C:\Program Files"—mostly C:\Program Files\Common Files\microsoft shared\ink\.

Where is the On-Screen Keyboard exe located?

Resolution: There is an application called "Microsoft Onscreen Keyboard" that is built-in to Windows. The specific name is "osk.exe" and it is located in the C:\Windows\system32 folder for 32 bit OS or c:\Windows\SysWow64\en-US for 64 bit Operating Systems.


2 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.

like image 72
torvin Avatar answered Sep 23 '22 14:09

torvin


I had the same problem too. It took me much time and headache, but Thanks to Alexei and Torvin I finally got it working on Win 10 1709. Visibility check was the difficulty. Maybe The OSKlib Nuget could be updated. Let me sum up the complete sulotion (For sure my code has some unnecessary lines now):

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using System.ComponentModel;  using Osklib.Interop; using System.Runtime.InteropServices; using System.Threading;  namespace OSK {     public static class OnScreenKeyboard     {         static OnScreenKeyboard()         {             var version = Environment.OSVersion.Version;             switch (version.Major)             {                 case 6:                     switch (version.Minor)                     {                         case 2:                             // Windows 10 (ok)                             break;                     }                     break;                 default:                     break;             }         }          private static void StartTabTip()         {             var p = Process.Start(@"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");             int handle = 0;             while ((handle = NativeMethods.FindWindow("IPTIP_Main_Window", "")) <= 0)             {                 Thread.Sleep(100);             }         }          public static void ToggleVisibility()         {             var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));             var instance = (ITipInvocation)Activator.CreateInstance(type);             instance.Toggle(NativeMethods.GetDesktopWindow());             Marshal.ReleaseComObject(instance);         }          public static void Show()         {             int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");             if (handle <= 0) // nothing found             {                 StartTabTip();                                 Thread.Sleep(100);                             }             // on some devices starting TabTip don't show keyboard, on some does  ¯\_(ツ)_/¯             if (!IsOpen())             {                 ToggleVisibility();             }         }          public static void Hide()         {             if (IsOpen())             {                 ToggleVisibility();             }         }                   public static bool Close()         {             // find it             int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");             bool active = handle > 0;             if (active)             {                 // don't check style - just close                 NativeMethods.SendMessage(handle, NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_CLOSE, 0);             }             return active;         }          public static bool IsOpen()         {             return GetIsOpen1709() ?? GetIsOpenLegacy();         }           [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);          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);         }      }       [ComImport]     [Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]     [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]     interface ITipInvocation     {         void Toggle(IntPtr hwnd);     }      internal static class NativeMethods     {         [DllImport("user32.dll", EntryPoint = "FindWindow")]         internal static extern int FindWindow(string lpClassName, string lpWindowName);          [DllImport("user32.dll", EntryPoint = "SendMessage")]         internal static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);          [DllImport("user32.dll", EntryPoint = "GetDesktopWindow", SetLastError = false)]         internal static extern IntPtr GetDesktopWindow();          [DllImport("user32.dll", EntryPoint = "GetWindowLong")]         internal static extern int GetWindowLong(int hWnd, int nIndex);          internal const int GWL_STYLE = -16;         internal const int GWL_EXSTYLE = -20;                 internal const int WM_SYSCOMMAND = 0x0112;         internal const int SC_CLOSE = 0xF060;          internal const int WS_DISABLED = 0x08000000;          internal const int WS_VISIBLE = 0x10000000;      } } 
like image 41
Lama Avatar answered Sep 22 '22 14:09

Lama