在周年纪念日更新之前的Windows 8和Windows 10中,可以通过启动
来显示触控键盘C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe
它不再适用于Windows 10周年更新; TabTip.exe
进程正在运行,但键盘未显示。
有没有办法以编程方式显示它?
更新
我找到了一个解决方法 - 用假鼠标点击系统托盘中的触摸键盘图标。这是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
答案 0 :(得分:22)
好的,当用户按下系统托盘中的按钮时,我反向设计了资源管理器的功能。
基本上它创建了一个未记录的接口ITipInvocation
的实例,并调用其Toggle(HWND)
方法,将桌面窗口作为参数传递。顾名思义,该方法根据其当前状态显示或隐藏键盘。
请注意该浏览器会在每次点击按钮时创建ITipInvocation
的实例。所以我认为实例不应该被缓存。我还注意到,资源管理器从不在获取的实例上调用Release()
。我对COM不太熟悉,但这看起来像个bug。
我在Windows 8.1,Windows 10和Windows中对此进行了测试Windows 10周年纪念版,它的工作完美。这是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;
}
这里也是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();
}
每个@EugeneK评论 更新:,我相信tabtip.exe
是有问题的COM组件的COM服务器,所以如果你的代码得到REGDB_E_CLASSNOTREG
,它可能应该是运行tabtip.exe
然后重试。
答案 1 :(得分:6)
我发现的唯一解决方案是发送PostMessage,就像你在回答1中提到的那样。这是C#版本,以防有人需要它。
[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,
}
答案 2 :(得分:5)
我尝试在Windows 10周年更新
上打开触摸键盘时检测到4种情况1 - 无所事事
2 + 3 - 通过COM激活
4 - 最有趣的场景。在一些设备启动TabTip过程打开触摸键盘,在一些 - 不是。所以我们必须启动TabTip进程,等待出现窗口&#34; IPTIP_Main_Window&#34;,检查它是否可见并通过COM激活它,如果有必要的话。
我为我的项目制作了一个小型库,你可以使用它 - osklib
答案 3 :(得分:5)
我也有同样的问题。我花了很多时间和头痛,但多亏了Alexei和Torvin,我终于让它在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'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 :(得分:4)
关于Windows 10周年更新如何设置触摸键盘仍然有些神秘感。我实际上有同样的问题,这里是我发现的最新信息:
Windows 10 1607有两种工作模式:桌面和平板电脑。在桌面模式下,可以调用TabTip.exe但不会显示。在平板电脑模式下,一切正常:TabTip.exe在调用时自动显示。因此,100%正常工作的解决方法是将您的计算机设置为平板电脑模式,但谁希望他的台式机/笔记本电脑能够以平板电脑模式工不管怎样,不是我!
您可以使用“EnableDesktopModeAutoInvoke
”键(HKCU,DWORD设置为1),在某些运行1607的计算机上,它在桌面模式下运行良好。但由于某些未知原因,它无法在我的HP触控板上运行。
请注意,此注册表值为“如果Windows参数中没有连接键盘,则在桌面模式下显示触摸键盘”&gt;触摸
到目前为止,已经在4台不同的计算机上进行了测试,我无法在所有计算机上正常运行...
答案 5 :(得分:3)
问题似乎与Windows操作系统的设置有关。我遇到了与我正在开发的应用程序相同的问题。使用Windows 8和10(更新前)代码,调用键盘工作正常,但更新后无法正常工作。阅读this article后,我做了以下事情:
按下Win + I打开“设置”应用
点击设备&gt;打字
已启用“当设备没有连接键盘时自动在窗口应用中显示触控键盘”。
该键盘开始在Windows 10中显示之后。
答案 6 :(得分:3)
在控件中实现IValueProvider / ITextProvider是实现此目的的正确方法,如下所述:https://stackoverflow.com/a/43886052/1184950
答案 7 :(得分:2)
以下代码将始终有效,因为它使用了最新的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;
}
答案 8 :(得分:0)
使用此方法:
创建osk.bat文件并将其保存在程序文件夹下即。 C:\My Software\osk.bat
在此osk.bat中键入以下cmd:
"C:\Program Files\Common Files\Microsoft Shared\Ink\Tabtip.exe"
使用Windows脚本运行此bat文件
oWSH = CREATEOBJECT("wscript.shell")
oWSH.Run("osk.bat", 0, .T.)
答案 9 :(得分:0)
我尝试了多种无效的方法。但是我发现可以使用Windows / Ctrl / O快捷键打开“屏幕键盘”。
还有一个Nuget包:Michael Noonan的Input Simulator。
如果您在Winforms项目中安装InputSimulator NuGet软件包-然后将这样的代码添加到事件中,例如按钮:
private void button1_Click(object sender, EventArgs e)
{
var simu = new InputSimulator();
simu.Keyboard.ModifiedKeyStroke(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.CONTROL }, VirtualKeyCode.VK_O);
}
您还需要添加以下using语句:
using WindowsInput;
using WindowsInput.Native;
运行您的应用程序,该按钮将显示键盘并再次按下它并将其删除。
我使用的是Windows 10和vs 2019。
答案 10 :(得分:-5)
在Win10 Ver 1803,DesktopMode中,没有可靠的方法
开启或关闭“触摸键盘” [ITipInvocation.Toggle()];
也不能可靠地发现它是否“向上”(在屏幕上)
[IFrameworkInputPane.Location()];这两个例程均随机失败。
相反,请确保“ TabTIP.EXE”和“ .... InputApp.EXE”
仅在键盘“向上”(在屏幕上)时运行。
要打开和关闭键盘(从Jeff-Relf.Me/X.ZIP中的X.CPP):
BranchA
BranchB
BranchC