web-dev-qa-db-fra.com

Afficher le clavier tactile (TabTip.exe) dans Windows 10 Anniversary Edition

Sous Windows 8 et Windows 10 avant la mise à jour anniversaire, il était possible d’afficher le clavier tactile en commençant par 

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

Il ne fonctionne plus dans la mise à jour Windows 10 Anniversary; le processus TabTip.exe est en cours d'exécution, mais le clavier n'est pas affiché.

Y a-t-il un moyen de le montrer par programme?

METTRE À JOUR

J'ai trouvé une solution de contournement - un faux clic de souris sur l'icône du clavier tactile dans la barre d'état système. Voici le code en 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;

MISE À JOUR 2

Une autre chose que j'ai trouvée est que la définition de cette clé de registre restaure une ancienne fonctionnalité lors du démarrage de TabTip.exe.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TabletTip\1.7\EnableDesktopModeAutoInvoke=1
29
EugeneK

OK, j’ai inversé ce que fait Explorer lorsque l’utilisateur appuie sur ce bouton dans la barre des tâches.

Fondamentalement, il crée une instance d'une interface non documentée ITipInvocation et appelle sa méthode Toggle(HWND), en passant la fenêtre du bureau en tant qu'argument. Comme son nom l'indique, la méthode affiche ou masque le clavier en fonction de son état actuel. 

Veuillez noter que l'explorateur crée une instance de ITipInvocation à chaque clic de bouton. Je pense donc que l'instance ne devrait pas être mise en cache. J'ai également remarqué qu'Explorer n'appelle jamais Release() sur l'instance obtenue. Je ne connais pas trop COM, mais cela ressemble à un bug.

J'ai testé cela dans Windows 8.1, Windows 10 et Windows 10 Anniversary Edition et cela fonctionne parfaitement. Voici un exemple minimal en C qui manque manifestement de contrôles d'erreur.

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

Voici également la version C #:

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

Mise à jour: par les commentaires de @EugeneK, je crois que tabtip.exe est le serveur COM du composant COM en question. Par conséquent, si votre code obtient REGDB_E_CLASSNOTREG, il devrait probablement exécuter tabtip.exe et réessayer.

21
torvin

La seule solution que j'ai trouvée fonctionne en envoyant à PostMessage comme vous l'avez mentionné dans la réponse 1. Voici la version C # de ce fichier au cas où quelqu'un en aurait besoin.

[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,
}
5
mikesl

J'ai eu le même problème aussi. Cela m'a pris beaucoup de temps et de maux de tête, mais grâce à Alexei et Torvin, je l’ai finalement fait travailler sur Win 10 1709. La vérification de la visibilité était la difficulté. Peut-être qu'OSKlib Nuget pourrait être mis à jour. Permettez-moi de résumer la sulotion complète (mon code comporte maintenant des lignes inutiles):

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;

    }
}
4
lama

Je détecte 4 situations lorsque j'essaie d'ouvrir Touch Keyboard sous Windows 10 Anniversary Update

  1. Le clavier est visible - lorsque "IPTIP_Main_Window" est présent, NOT est désactivé et EST visible.
  2. Le clavier n'est pas visible - lorsque "IPTIP_Main_Window" est présent mais désactivé
  3. Le clavier n'est pas visible - lorsque "IPTIP_Main_Window" est présent mais PAS désactivé et PAS visible
  4. Le clavier n'est pas visible - lorsque "IPTIP_Main_Window" est PAS présent

1 - rien à faire

2 + 3 - activation via COM

4 - scénario le plus intéressant. Sur certains appareils, le démarrage du processus TabTip ouvre le clavier tactile, sur d'autres pas. Nous devons donc démarrer le processus TabTip, attendre la fenêtre "IPTIP_Main_Window", vérifier sa visibilité et l’activer via COM si nécessaire.

Je fais une petite bibliothèque pour mon projet, vous pouvez l'utiliser - osklib

4

La manière dont le clavier tactile est défini comme visible dans la mise à jour anniversaire de Windows 10 reste encore mystérieuse. J'ai en fait exactement le même problème et voici les dernières informations que j'ai trouvées:

  • Windows 10 1607 fonctionne dans deux modes: Desktop et Tablet. En mode Bureau, TabTip.exe peut être appelé mais ne sera pas affiché. En mode tablette, tout fonctionne correctement: TabTip.exe se montre lorsqu'il est appelé. Donc, une solution de travail à 100% consiste à configurer votre ordinateur en mode tablette, mais qui veut que son ordinateur de bureau/ordinateur portable fonctionne en mode tablette? Pas moi quand même!

  • Vous pouvez utiliser la clé "EnableDesktopModeAutoInvoke" (HKCU, DWORD définie sur 1) et sur certains ordinateurs exécutant 1607, elle fonctionnait parfaitement en mode Bureau. Mais pour des raisons inconnues, cela ne fonctionne pas sur mon pavé tactile HP.

Veuillez noter que cette valeur de registre est l'option "Afficher le clavier tactile en mode Bureau s'il n'y a pas de clavier connecté" dans les paramètres Windows> touch  

  • Vous pouvez utiliser le code de Torvin pour afficher TabTip.exe (comme mentionné, TabTip.exe devrait être en cours d'exécution lorsque vous effectuez le travail de COM), il fonctionne correctement sur certains ordinateurs exécutant 1607 (y compris mon pavé tactile HP! Yay!). Mais cela ne fera rien. sur quelques autres compositions avec les mêmes fenêtres Build.

Jusqu'ici testé sur 4 ordinateurs différents et je suis incapable d'obtenir quelque chose qui fonctionne bien sur tous ...

4
Usul

L'implémentation de IValueProvider/ITextProvider dans votre contrôle est un moyen correct d'y parvenir, comme décrit ici: https://stackoverflow.com/a/43886052/1184950

3
tombam

Le problème semble être lié au réglage du système d'exploitation Windows. J'ai rencontré le même problème avec l'application que je développais. Avec Windows 8 et 10 (avant la mise à jour), le code appelé clavier fonctionnait correctement, mais ne fonctionnait pas après la mise à jour. Après avoir lu cet article , j’ai suivi:

  1. Appuyez sur Win + I pour ouvrir l'application Paramètres

  2. Cliqué sur Périphériques> Dactylographie 

  3. Tourné "Afficher automatiquement le clavier tactile dans les applications fenêtrées quand aucun clavier n'est connecté à votre appareil" ON.

    Juste après que le clavier commence à apparaître dans Windows 10 également.

3
user3480038

Le code suivant fonctionnera toujours, car il utilise la dernière version de MS Api
Je le mets dans une dll (nécessaire pour un projet Delphi) mais c’est un simple C
Aussi utile pour obtenir la taille du clavier et ajuster la disposition de l'application

//*******************************************************************
//
// RETURNS KEYBOARD RECTANGLE OR EMPTY ONE IF KEYBOARD IS NOT VISIBLE
//
//*******************************************************************
RECT __stdcall  GetKeyboardRect()
{
    IFrameworkInputPane *inputPane = NULL;
    RECT prcInputPaneScreenLocation = { 0,0,0,0 };
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);    
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(CLSID_FrameworkInputPane, NULL, CLSCTX_INPROC_SERVER, IID_IFrameworkInputPane, (LPVOID*)&inputPane);
        if (SUCCEEDED(hr))
        {
            hr=inputPane->Location(&prcInputPaneScreenLocation);
            if (!SUCCEEDED(hr))
            {                   
            }
            inputPane->Release();
        }
    }       
    CoUninitialize();   
    return prcInputPaneScreenLocation;
}
2
Sevast