visual studio programming para microsoft development developing developer apps windows winapi touch windows-10 windows-10-desktop

studio - Mostrar teclado táctil(TabTip.exe) en la edición de Windows 10 Anniversary



windows 10 mobile development (10)

Bien, invierto la ingeniería de lo que hace el explorador cuando el usuario presiona ese botón en la bandeja del sistema.

Básicamente, crea una instancia de una interfaz no documentada ITipInvocation y llama a su método Toggle(HWND) , pasando la ventana del escritorio como un argumento. Como su nombre indica, el método muestra u oculta el teclado dependiendo de su estado actual.

Tenga en cuenta que el explorador crea una instancia de ITipInvocation en cada clic del botón. Por lo tanto, creo que la instancia no se debe almacenar en caché. También noté que el explorador nunca llama a Release() en la instancia obtenida. No estoy muy familiarizado con COM, pero parece un error.

Probé esto en Windows 8.1, Windows 10 y Windows 10 Anniversary Edition y funciona perfectamente. Aquí hay un ejemplo mínimo en C que obviamente carece de algunas verificaciones de error.

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

Aquí está la versión de C # también:

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

Actualización: según los comentarios de @EugeneK, creo que tabtip.exe es el servidor COM para el componente COM en cuestión, por lo que si su código recibe REGDB_E_CLASSNOTREG , probablemente debería ejecutar tabtip.exe y volver a intentarlo.

En Windows 8 y Windows 10 antes de la actualización de aniversario, era posible mostrar el teclado táctil comenzando

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

Ya no funciona en la actualización de aniversario de Windows 10; el proceso TabTip.exe se está ejecutando, pero el teclado no se muestra.

¿Hay alguna manera de mostrarlo programáticamente?

ACTUALIZAR

Encontré una solución alternativa: haga clic con el mouse falso en el icono del teclado táctil en la bandeja del sistema. Aquí está el código 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;

ACTUALIZACIÓN 2

Otra cosa que encontré es que establecer esta clave de registro restaura la funcionalidad anterior al iniciar TabTip.exe muestra el teclado táctil

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


Detecto 4 situaciones cuando intento abrir el Teclado táctil en Windows 10 Anniversary Update

  1. El teclado es visible: cuando "IPTIP_Main_Window" está presente, NO está desactivado y ES visible
  2. El teclado no está visible, cuando "IPTIP_Main_Window" está presente pero deshabilitado
  3. El teclado no está visible, cuando "IPTIP_Main_Window" está presente pero NO está desactivado y NO es visible
  4. El teclado no está visible, cuando "IPTIP_Main_Window" NO está presente

1 - nada que hacer

2 + 3 - activando a través de COM

4 - escenario más interesante. En algunos dispositivos, el inicio del proceso de TabTip abre el teclado táctil, en algunos casos, no. Por lo tanto, debemos iniciar el proceso TabTip, esperar a que aparezca la ventana "IPTIP_Main_Window", verificar su visibilidad y activarlo a través de COM si es necesario.

Hago una pequeña biblioteca para mi proyecto, puedes usarla - osklib


El problema parece ser con la configuración del sistema operativo Windows. He enfrentado el mismo problema con la aplicación que estaba desarrollando. Con Windows 8 y 10 (antes de la actualización) el código que llamaba al teclado funcionó bien, pero después de la actualización no funcionó. Después de leer este artículo , hice lo siguiente:

  1. Presioné Win + I para abrir la aplicación de configuración

  2. Haz clic en Dispositivos> Escribir

  3. Activado " Mostrar automáticamente el teclado táctil en aplicaciones con ventana cuando no hay ningún teclado conectado a su dispositivo " ENCENDIDO.

    Justo después de que el teclado comenzara a aparecer también en Windows 10.


El siguiente código siempre funcionará ya que usa la última MS Api
Lo puse en un dll (Necesario para un proyecto Delphi) pero es un simple C
También es útil para obtener el tamaño del teclado y ajustar el diseño de la aplicación

//******************************************************************* // // 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; }


En Win10 Ver 1803, DesktopMode, no hay una manera confiable de
alternar el "Teclado táctil" en | off [ITipInvocation.Toggle ()];
ni puede descubrir de manera confiable si está "arriba" (en la pantalla)
[IFrameworkInputPane.Location ()]; Ambas rutinas fallan aleatoriamente .

En su lugar, asegúrese de que "TabTIP.EXE" y ".... InputApp.EXE"
solo se ejecuta cuando el teclado está "arriba" (en la pantalla).

Para activar y desactivar el teclado (desde X.CPP en Jeff-Relf.Me/X.ZIP):

if ( WM == WM_HOTKEY && C == ''K'' ) { // A mouse button takes me here. Jeff-Relf.Me/g600.PNG if ( KillProc = 1, Running( L"TabTIP.EXE" ), KillProc = 1, Running( L"WindowsInternal.ComposableShell.Experiences.TextInput.InputApp.EXE" ) ) // The keyboard was _On_ ( i.e. its processes were running ), // so it was "turned _Off_" (killed); and we''re done. goto Done ; // The keyboard was _Off_ ( i.e. no running processes ). // Turn it _On_: Launch( L"%CommonProgramFiles%/microsoft shared/ink/TabTIP.EXE" ); Sleep(99); static const GUID CLSID_UIHostNoLaunch = { 0x4CE576FA, 0x83DC, 0x4f88, 0x95, 0x1C, 0x9D, 0x07, 0x82, 0xB4, 0xE3, 0x76 }; static const GUID IID_ITipInvocation = { 0x37c994e7, 0x432b, 0x4834, 0xa2, 0xf7, 0xdc, 0xe1, 0xf1, 0x3b, 0x83, 0x4b }; static struct ITipInvocation : IUnknown { virtual HRESULT STDMETHODCALLTYPE Toggle( HWND wnd ) = 0 ; } * Tog ; Tog = 0, CoCreateInstance( CLSID_UIHostNoLaunch, 0, CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER, IID_ITipInvocation, (void**) & Tog ); // Firefox and Chrome need this: Tog ? Tog->Toggle( GetDesktopWindow() ), Tog->Release() : 0 ; } - - - - - - - - - - - - - // To get the process list, and kill stuff: #include <tlhelp32.H> int KillProc ; int Running( wchar * EXE ) { int Found ; HANDLE PIDs, aProc ; PROCESSENTRY32 aPID = { sizeof aPID }; PIDs = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); Process32First( PIDs, &aPID ); while ( Found = !strCmpI( aPID.szExeFile, EXE ), KillProc && Found && ( aProc = OpenProcess( PROCESS_TERMINATE, 0, aPID.th32ProcessID ), aProc ? TerminateProcess( aProc, 9 ), CloseHandle( aProc ) : 0 ), !Found && Process32Next( PIDs, &aPID ) ); KillProc = 0, CloseHandle( PIDs ); return Found ; } Launch( wchar * Cmd ) { wchar _Cmd[333]; static PROCESS_INFORMATION Stat ; static STARTUPINFO SU = { sizeof SU }; SetEnvironmentVariable( L"__compat_layer", L"RunAsInvoker" ); ExpandEnvironmentStrings( Cmd, _Cmd, 333 ), Cmd = _Cmd ; if ( CreateProcess( 0, Cmd, 0,0,1,0,0,0, &SU , &Stat ) ) CloseHandle( Stat.hProcess ), CloseHandle( Stat.hThread ); } // CoInitialize(0);


Implementar IValueProvider / ITextProvider en su control es una forma correcta de lograr esto, como se describe aquí: https://.com/a/43886052/1184950


La única solución que he encontrado que funciona es enviar PostMessage como lo mencionó en la respuesta 1. Aquí está la versión de C # en caso de que alguien lo necesite.

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


Todavía hay algún misterio acerca de cómo el teclado táctil se establece visible por Windows 10 Anniversary Update. De hecho, estoy teniendo el mismo problema y aquí están las últimas noticias que he encontrado:

  • Windows 10 1607 funciona en dos modos: escritorio y tableta. Mientras está en el modo Escritorio, se puede llamar a TabTip.exe pero no se mostrará. Mientras está en modo Tablet, todo funciona bien: TabTip.exe se muestra cuando se le llama. Entonces, ¿una solución al 100% de trabajo es configurar su computadora en modo Tablet pero ¿quién quiere que su computadora de escritorio / portátil funcione en modo tableta? ¡Yo no de todos modos!

  • Puede utilizar la clave " EnableDesktopModeAutoInvoke " (HKCU, DWORD establecido en 1) y en algunas computadoras que ejecutan 1607 funcionó de maravilla en el modo de escritorio. Pero por algún motivo desconocido, no funciona en mi panel táctil HP.

Tenga en cuenta que este valor de registro es la opción "Mostrar teclado táctil en el modo de escritorio si no hay un teclado conectado" en los parámetros de Windows> táctil

  • Puede usar el código de Torvin para mostrar TabTip.exe (como se mencionó, TabTip.exe debería estar ejecutándose cuando hace las cosas COM), funciona bien en algunas computadoras que ejecutan 1607 (¡incluyendo mi panel táctil HP! ¡Sí!) Pero no hará nada en algunos otros comps con las mismas ventanas Build.

Hasta ahora probado en 4 computadoras diferentes y no puedo hacer que todo funcione bien en todos ...


Tuve el mismo problema también. Me tomó mucho tiempo y dolor de cabeza, pero gracias a Alexei y Torvin finalmente lo conseguí trabajando en Win 10 1709. El control de visibilidad fue la dificultad. Tal vez el OSKlib Nuget podría actualizarse. Permítanme resumir el sulotion completo (seguro que mi código ahora tiene algunas líneas innecesarias):

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


Usa este método:

  1. Cree el archivo osk.bat y guárdelo en su carpeta de programa, es decir. C:/My Software/osk.bat

  2. Escriba en este osk.bat el siguiente cmd:

    "C:/Program Files/Common Files/Microsoft Shared/Ink/Tabtip.exe"

  3. Use Windows Script para ejecutar este archivo bat

    oWSH = CREATEOBJECT("wscript.shell")

    oWSH.Run("osk.bat", 0, .T.)