我正在尝试解决我们的C# Prism WPF应用程序中的屏幕键盘问题,该应用程序针对通过ClickOnce部署的Windows4.5.2,运行在Windows101803版本上。
我们的应用程序有一个触发事件的TimeOut函数,在这种情况下,我想切换键盘的可见性或关闭它(如果它是打开的)。在下面的类中的Close()函数有时不工作,我在日志中看到(在下面的代码中没有显示)应用程序找到句柄( is>0)并在其上发出close命令,但键盘没有关闭。
这个问题与下面提到并链接的两个问题有关!但是我不能评论,所以我不得不问一个新的问题。
在Determine if Windows 10 Touch Keyboard is Visible or Hidden之前,我们在v1709中提出了一种可行的解决方案
现在我正在尝试实现这个问题的最新帖子,以便能够在超时或应用程序关闭事件时切换键盘的可见性,如果它检测到键盘是打开的,我正在尝试将其与您在下面看到的keyboard类相结合,但我遇到了问题。
因此,我们有来自上面的post和Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition的组合的触摸键盘类:
static class TouchKeyboard
{
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 GetIsOpen()
{
return GetIsOpen1709() ?? GetIsOpenLegacy();
}
private static bool? GetIsOpen1709()
{
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
var 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);
}
[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);
这在1709年工作得还不错,但在1803年就不行了,而且新的客户要求在超时时关闭键盘。因此,现在我已经将stackoverflow问题的最新帖子中的代码作为函数添加到上面的类中(我不认为这是正确的方法):
static class TouchKeyboard
{
///Above code omitted
public static bool GetIsOpen1803()
{
// do this once:
var brokerClass = new ImmersiveShellBroker();
var broker = (IImmersiveShellBroker)brokerClass;
var ihm = broker.GetInputHostManagerBroker();
Marshal.ReleaseComObject(broker);
// now ihm reference can be cached and used later:
Rect rect;
DisplayMode mode;
ihm.GetIhmLocation(out rect, out mode);
}
[ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
class ImmersiveShellBroker
{
}
[ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IImmersiveShellBroker
{
void Dummy();
IInputHostManagerBroker GetInputHostManagerBroker();
}
[ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IInputHostManagerBroker
{
void GetIhmLocation(out Rect rect, out DisplayMode mode);
}
[StructLayout(LayoutKind.Sequential)]
struct Rect
{
public int Left, Top, Right, Bottom;
}
enum DisplayMode
{
NotSupported = 0,
Floating = 2,
Docked = 3,
}
}
我认为我没有正确地实现它,因为它并不总是有效,当我从上面提到的超时事件调用GetIsOpen1803时,当我看到键盘打开时,它有时会返回矩形0,0,0,0。在代码中,注释中写着“只做一次”和“可以缓存并在以后使用”,但我似乎不能掌握如何在我们的Touchkeyboard类中做到这一点。
到目前为止,如果键盘在应用程序中的任何位置出现在屏幕上,并且能够关闭它,那么让GetIsOpen方法可靠地返回它是我所不能做到的。即使终止所有tabtip进程,也不一定会关闭键盘!唯一有效的方法是终止服务,但我不能这样做,因为需要提升权限。
因此,找到一种可靠的方法来查看键盘是否对所有的windows10版本都是打开的,以及一种可靠的方法来关闭键盘或切换键盘的可见性(如果有的话!),这绝对是我的一天。
任何帮助都将不胜感激!
发布于 2018-06-24 05:22:35
你可以试试我的库- https://github.com/AlexeiScherbakov/osklib的更新版本吗?
您可以将OnScreenKeyboardWatcher/DispatcherOnScreenKeyboardWatcher/WinFormsKeyboardWatcher用于: 1.检测当前键盘状态2.观察键盘打开多长时间(超时)
我已经在旧版本(1709,1703和1607)上测试了这段代码,我的1803 (build 17134)也可以工作
发布于 2018-09-10 20:47:20
使用所提供的解决方案,我们无法让键盘可靠地工作。对我们有效的唯一(也是简单的)解决方案是升级到NuGetFramework4.7.2,并使用它升级我们解决方案中的所有.Net包。我们不能在几个月前使用4.7.2,因为ClickOnce没有正确的内置引用,这在最近的更新中得到了解决。
在我们升级到4.7.2之后,我们从我们的应用程序中删除了所有与键盘相关的代码,现在一切都像预期的那样工作(在平板电脑模式下,这是我们想要的),没有来自我们端的任何代码,我想补充一下!所以我没有发布任何代码的原因很简单,因为没有任何代码!
发布于 2018-07-27 07:12:55
正如Tim Bijnens所指出的,当他启动这个线程时,键盘切换( ITipInvocation.Toggle() )并不总是弹出键盘。
在Win10版本1803,DesktopMode中,没有可靠的方法来
切换“触摸键盘”开|关ITipInvocation.Toggle();
你也不能可靠地发现它是否“向上”(在屏幕上)
IFrameworkInputPane.Location();两个例程都会随机失败。
相反,请确保"TabTIP.EXE“和"....InputApp.EXE”
仅当键盘“打开”(在屏幕上)时运行;请参见:
https://stackoverflow.com/questions/50623154
复制相似问题