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
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.
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,
}
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;
}
}
Je détecte 4 situations lorsque j'essaie d'ouvrir Touch Keyboard sous Windows 10 Anniversary Update
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
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
Jusqu'ici testé sur 4 ordinateurs différents et je suis incapable d'obtenir quelque chose qui fonctionne bien sur tous ...
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
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:
Appuyez sur Win + I pour ouvrir l'application Paramètres
Cliqué sur Périphériques> Dactylographie
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.
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;
}