38 votes

Afficher le clavier tactile (TabTip.exe) dans l'édition Anniversaire de Windows 10

Dans Windows 8 et Windows 10 avant la mise à jour Anniversary, il était possible d'afficher le clavier tactile en démarrant

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

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

Y a-t-il un moyen de l'afficher de manière programmatique?

MISE À JOUR

J'ai trouvé une solution de contournement - un clic de souris simulé sur l'icône du clavier tactile dans la barre système. Voici le code en Delphi

// Trouver la fenêtre de l'icône de la barre système
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;

// Envoyer des messages de clic de souris
TrayButtonWindow := FindTrayButtonWindow;
if TrayButtonWindow > 0 then
begin
  PostMessage(TrayButtonWindow, WM_LBUTTONDOWN, MK_LBUTTON, $00010001);
  PostMessage(TrayButtonWindow, WM_LBUTTONUP, 0, $00010001);
end;

MISE À JOUR 2

Autre chose que j'ai trouvée est que le réglage de cette clé de registre restaure l'ancienne fonctionnalité où le démarrage de TabTip.exe montre le clavier tactile

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

1 votes

Avec chaque question posée à propos de TabTip.exe, je dois me demander : y a-t-il une API qui fait apparaître le clavier tactile de manière prise en charge ?

0 votes

Je préférerais utiliser une API si possible, mais je n'en ai pas trouvé. Et toutes les réponses sur SO font référence à TabTip.exe

1 votes

La recherche de TabTip.exe sur MSDN amène cela; est-ce correct? Sinon, le panneau d'entrée dont parle Raymond Chen ici est-il le même que celui fourni par TabTip.exe? Je ne peux pas vérifier les deux en ce moment.

32voto

torvin Points 322

D'accord, j'ai inversé ce que fait l'explorateur lorsque l'utilisateur appuie sur ce bouton dans la barre système.

Essentiellement, 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 comme 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 sur le bouton. Donc, je pense que l'instance ne doit pas être mise en cache. J'ai également remarqué que l'explorateur n'appelle jamais Release() sur l'instance obtenue. Je ne suis pas très familier avec COM, mais cela ressemble à un bug.

J'ai testé cela sous Windows 8.1, Windows 10 et Windows 10 Anniversary Edition et cela fonctionne parfaitement. Voici un exemple minimal en C qui manque évidemment certaines vérifications 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 en 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 : selon les commentaires de @EugeneK, je crois que tabtip.exe est le serveur COM pour le composant COM en question, donc si votre code obtient REGDB_E_CLASSNOTREG, il devrait probablement exécuter tabtip.exe et réessayer.

1 votes

Cela ne fonctionne que pour moi si TabTip.exe est en cours d'exécution, sans ce processus en cours d'exécution, cela échoue avec une erreur de 'Classe non enregistrée'.

0 votes

Merci, cela fonctionne très bien. Y a-t-il un moyen de contrôler sa visibilité de manière élégante?

2 votes

Merci. Je cherche maintenant un moyen de déterminer et de contrôler la visibilité aussi. L'ancienne "hwnd = FindWindow("IPTip_Main_Window", NULL)" n'est plus complètement efficace. Tester le HWND retourné pour null / visibilité renvoie toujours vrai. Cependant "PostMessage(hwnd, WM_SYSCOMMAND, (int)SC_CLOSE, 0)" cachera toujours le OSK.

10voto

lama Points 59

J'ai également eu le même problème. Cela m'a pris beaucoup de temps et de maux de tête, mais grâce à Alexei et Torvin, j'ai finalement réussi à le faire fonctionner sur Win 10 1709. La vérification de la visibilité était la difficulté. Peut-être que le paquet Nuget OSKlib pourrait être mis à jour. Permettez-moi de résumer la solution complète (Sûrement, mon code a maintenant certaines lignes inutiles) :

utilisant System;
utilisant System.Collections.Generic;
utilisant System.Linq;
utilisant System.Text;
utilisant System.Diagnostics;
utilisant System.ComponentModel;

utilisant Osklib.Interop;
utilisant System.Runtime.InteropServices;
utilisant System.Threading;

espace de noms OSK
{
    public static class ClavierÀL'écran
    {
        static ClavierÀL'écran()
        {
            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 DémarrerTabTip()
        {
            var p = Process.Start(@"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
            int poignée = 0;
            while ((poignée = NativeMethods.FindWindow("IPTIP_Main_Window", "")) <= 0)
            {
                Thread.Sleep(100);
            }
        }

        public static void BasculeDeVisibilité()
        {
            var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));
            var instance = (ITipInvocation)Activator.CreateInstance(type);
            instance.Bascule(NativeMethods.GetDesktopWindow());
            Marshal.ReleaseComObject(instance);
        }

        public static void Afficher()
        {
            int poignée = NativeMethods.FindWindow("IPTIP_Main_Window", "");
            if (poignée <= 0) // rien trouvé
            {
                DémarrerTabTip();                
                Thread.Sleep(100);                
            }
            // sur certains appareils, le démarrage de TabTip ne montre pas le clavier, sur certains si ¯\_()_/¯
            if (!EstOuvert())
            {
                BasculeDeVisibilité();
            }
        }

        public static void Masquer()
        {
            if (EstOuvert())
            {
                BasculeDeVisibilité();
            }
        }        

        public static bool Fermer()
        {
            // le trouver
            int poignée = NativeMethods.FindWindow("IPTIP_Main_Window", "");
            bool actif = poignée > 0;
            if (actif)
            {
                // ne pas vérifier le style - juste fermer
                NativeMethods.SendMessage(poignée, NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_CLOSE, 0);
            }
            return actif;
        }

        public static bool EstOuvert()
        {
            return ObtenirEstOuvert1709() ?? ObtenirEstOuvertLegacy();
        }

        [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? ObtenirEstOuvert1709()
        {
            // s'il y a une fenêtre de premier plan - le clavier est fermé
            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; // plus de fenêtres, état du clavier inconnu

                // s'il s'agit d'un enfant d'une fenêtre WindowParentClass1709 - le clavier est ouvert
                wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
                if (wnd != IntPtr.Zero)
                    return true;
            }
        }

        private static bool ObtenirEstOuvertLegacy()
        {
            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 Bascule(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;

    }
}

0 votes

J'ai testé et il n'a pas détecté que TapTip était ouvert. Windows 10 Pro x64. ProcessExplorer: C:\Windows\SystemApps\InputApp_cw5n1h2txyewy\WindowsInternal‌​.ComposableShell.Exp‌​eriences.TextInput.I‌​nputApp.exe

6voto

mikesl Points 983

La seule solution que j'ai trouvée pour fonctionner est d'envoyer PostMessage comme vous l'avez mentionné dans la réponse 1. Voici la version C# de cela 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,
}

0 votes

Merci mikesl, ça a parfaitement fonctionné. Juste manquant la déclaration pour nullIntPtr que j'ai instanciée avec var nullIntPtr = IntPtr.Zero;

2 votes

Tandis que cela fonctionne, cela nécessite que l'icône de la barre d'outils soit disponible. Malheureusement, il peut être masqué par l'utilisateur.

0 votes

@mikesl comment pouvez-vous l'utiliser pour afficher uniquement le clavier téléphonique

6voto

Je détecte 4 situations en essayant d'ouvrir le clavier tactile sur la mise à jour Anniversaire de Windows 10

  1. Clavier est visible - lorsque "IPTIP_Main_Window" est présent, PAS désactivé et EST visible
  2. Clavier n'est pas visible - lorsque "IPTIP_Main_Window" est présent mais désactivé
  3. Clavier n'est pas visible - lorsque "IPTIP_Main_Window" est présent mais PAS désactivé et PAS visible
  4. 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 non. Nous devons donc démarrer le processus TabTip, attendre l'apparition de la fenêtre "IPTIP_Main_Window", la vérifier pour la visibilité et l'activer via COM si nécessaire.

J'ai créé une petite bibliothèque pour mon projet, vous pouvez l'utiliser - osklib

0 votes

Je pense que le comportement variable entre l'ouverture/la fermeture du clavier est lié au paramètre EnableDesktopModeAutoInvoke dans le registre. Merci pour la recherche et la bibliothèque !

4voto

Usul Points 84

Il reste encore un peu de mystère sur la façon dont le clavier tactile est rendu visible par la mise à jour Anniversaire de Windows 10. J'ai en fait le même problème et voici les dernières informations que j'ai trouvées :

  • Windows 10 1607 fonctionne en deux modes : Bureau et Tablette. En mode Bureau, TabTip.exe peut être appelé mais ne s'affiche pas. En mode Tablette, tout fonctionne bien : TabTip.exe s'affiche lorsqu'il est appelé. Donc une solution de contournement à 100 % fonctionnelle est de mettre votre ordinateur en mode Tablette, mais qui veut que son ordinateur de bureau/portable fonctionne en mode tablette ? Pas moi en tout cas !

  • Vous pouvez utiliser la clé "EnableDesktopModeAutoInvoke" (HKCU, DWORD défini sur 1) et sur certains ordinateurs exécutant 1607, cela a très bien fonctionné en mode Bureau. Mais pour des raisons inconnues, cela ne fonctionne pas sur mon touchpad 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 attaché" dans les paramètres de Windows > tactile

  • Vous pouvez utiliser le code de Torvin pour afficher TabTip.exe (comme mentionné TabTip.exe devrait être en cours d'exécution lorsque vous faites des opérations COM), cela fonctionne bien sur certains ordinateurs exécutant 1607 (y compris mon touchpad HP ! Hourra !) Mais cela ne fera rien sur d'autres ordinateurs avec la même version de Windows.

Jusqu'à présent testé sur 4 ordinateurs différents et je ne parviens pas à obtenir quelque chose qui fonctionne bien sur tous...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X