Показать сенсорную клавиатуру (TabTip.exe) в выпуске Windows 10 Anniversary
В Windows 8 и Windows 10 до обновления Anniversary появилась возможность показывать сенсорную клавиатуру, запустив
C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe
Он больше не работает в обновлении Windows 10 Anniversary; выполняется процесс TabTip.exe
, но клавиатура не отображается.
Есть ли способ показать это программно?
UPDATE
Я нашел обходное решение - поддельный щелчок мышью на значке сенсорной клавиатуры в системном трее. Вот код в 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;
ОБНОВЛЕНИЕ 2
Еще одна вещь, которую я обнаружил, заключается в том, что установка этого ключа реестра восстанавливает старые функции при запуске TabTip.exe показывает сенсорную клавиатуру
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\TabletTip\1.7\EnableDesktopModeAutoInvoke=1
Ответы
Ответ 1
ОК, я обратил внимание на то, что делает исследователь, когда пользователь нажимает эту кнопку на панели задач.
В основном он создает экземпляр недокументированного интерфейса ITipInvocation
и вызывает его метод Toggle(HWND)
, передавая окно рабочего стола в качестве аргумента. Как следует из названия, метод показывает или скрывает клавиатуру в зависимости от ее текущего состояния.
Обратите внимание, что проводник создает экземпляр ITipInvocation
при каждом нажатии кнопки. Поэтому я считаю, что экземпляр не должен кэшироваться. Я также заметил, что проводник никогда не вызывает Release()
в полученном экземпляре. Я не слишком хорошо знаком с COM, но это выглядит как ошибка.
Я тестировал это в Windows 8.1, Windows 10 и Windows 10 Anniversary Edition, и он отлично работает. Здесь минимальный пример в C, который, очевидно, не имеет некоторых проверок ошибок.
#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;
}
Здесь также версия С#:
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();
}
Обновление: за комментарии @EugeneK, я считаю, что tabtip.exe
является COM-сервером для рассматриваемого COM-компонента, поэтому, если ваш код получает REGDB_E_CLASSNOTREG
, он должен, вероятно, запустить tabtip.exe
и повторите попытку.
Ответ 2
Единственное решение, которое я нашел для работы, - это отправить PostMessage, как вы упомянули в ответ 1. Вот его версия С#, если кому-то это понадобится.
[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,
}
Ответ 3
У меня была такая же проблема. Мне потребовалось много времени и головной боли, но благодаря Алексею и Торвину я, наконец, получил работу над Win 10 1709. Видимость была сложностью. Возможно, OSKlib Nuget может быть обновлен. Позвольте мне подвести итог всей сулоции (наверняка у моего кода есть лишние строки):
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 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
Я обнаружил 4 ситуации при попытке открыть сенсорную клавиатуру в Windows 10 Anniversary Update
- Клавиатура видна - когда присутствует "IPTIP_Main_Window", НЕ отключено и IS видно
- Клавиатура не видна - когда "IPTIP_Main_Window" присутствует, но отключен
- Клавиатура не видна - когда присутствует "IPTIP_Main_Window", но НЕ отключен и НЕ виден
- Клавиатура не видна - когда "IPTIP_Main_Window" НЕ присутствует
1 - ничего не делать
2 + 3 - активация через COM
4 - самый интересный сценарий. На некоторых устройствах запуск TabTip открывает сенсорную клавиатуру, на некоторых - нет. Поэтому мы должны запустить процесс TabTip, дождаться появления окна "IPTIP_Main_Window", проверить его на видимость и активировать через COM, если это необходимо.
Я делаю небольшую библиотеку для своего проекта, вы можете использовать ее - osklib
Ответ 5
Есть еще какая-то загадка о том, как сенсорная клавиатура отображается видимым обновлением Windows 10 Anniversary. У меня на самом деле такая же проблема, и вот последняя информация, которую я нашел:
-
Windows 10 1607 работает в двух режимах: Desktop и Tablet. В режиме рабочего стола TabTip.exe можно вызывать, но не показывать. В режиме Tablet все работает нормально: TabTip.exe показывает себя при вызове. Таким образом, 100% -ный рабочий способ заключается в том, чтобы настроить компьютер в режиме Tablet Mode, но кто хочет, чтобы его рабочий стол/ноутбук работал в режиме планшета? Не все равно!
-
Вы можете использовать ключ "EnableDesktopModeAutoInvoke
" (HKCU, DWORD установлен в 1), а на некоторых компьютерах с рабочим номером 1607 он отлично работает в режиме рабочего стола. Но по неизвестным причинам он не работает на сенсорной панели HP.
Обратите внимание, что это значение реестра - это "Показать сенсорную клавиатуру в режиме рабочего стола, если в настройках Windows нет установленной клавиатуры" > коснитесь
- Вы можете использовать код Torvin для отображения TabTip.exe(как упоминалось, TabTip.exe должен работать, когда вы делаете COM-материал), он отлично работает на некоторых компьютерах с 1607 (включая мой тачпад HP! yay!) Но это ничего не сделает с некоторыми другими компиляторами с теми же строками Windows.
До сих пор тестировалось на 4 разных компьютерах, и я не могу заставить что-то нормально работать на всех...
Ответ 6
Проблема заключается в настройке ОС Windows. Я столкнулся с такой же проблемой с приложением, которое я разрабатывал. В Windows 8 и 10 (до обновления) код, который назвал клавиатуру, работал нормально, но после того, как обновление не сработало. После прочтения этой статьи, я сделал следующее:
-
Нажмите клавишу Win + I, чтобы открыть приложение "Настройки"
-
Нажмите "Устройства" > "Enter"
-
Повернуто " Автоматически отображать сенсорную клавиатуру в окнах приложений, когда на клавиатуре не установлена клавиатура.
Сразу после того, как клавиатура начнет появляться в Windows 10.
Ответ 7
Реализация IValueProvider/ITextProvider в вашем контроле является правильным способом достижения этого, как описано здесь: fooobar.com/questions/166363/...
Ответ 8
Следующий код всегда будет работать, поскольку он использует последнюю версию MS Api
Я поместил его в dll (необходимо для проекта Delphi), но это простой C
Также полезно для получения размера клавиатуры и настройки макета приложения
//*******************************************************************
//
// 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;
}
Ответ 9
Используйте этот метод:
-
Создайте файл osk.bat и сохраните его в своей папке программы, т.е. C:\My Software\osk.bat
-
Введите в этом osk.bat следующий cmd:
"C:\Program Files\Common Files\Microsoft Shared\Ink\Tabtip.exe"
-
Используйте Windows Script для запуска этого файла bat
oWSH = CREATEOBJECT("wscript.shell")
oWSH.Run("osk.bat", 0,.T.)
Ответ 10
В Win10 Ver 1803 DesktopMode нет надежного способа
переключить "Touch Keyboard" на | off [ITipInvocation.Toggle()];
и вы не можете надежно обнаружить, если он "вверх" (на экране)
[IFrameworkInputPane.Location()]; обе процедуры прерываются случайным образом.
Вместо этого убедитесь, что "TabTIP.EXE" и ".... InputApp.EXE"
запускаться только тогда, когда клавиатура "вверх" (на экране).
Для включения и выключения клавиатуры (из X.CPP в 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);