Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

winapi - 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 FilesCommon Filesmicrosoft sharedinkTabTip.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_MACHINESOFTWAREMicrosoftTabletTip1.7EnableDesktopModeAutoInvoke=1
Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...