343 lines
12 KiB
C#
343 lines
12 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Linq;
|
|||
|
using System.Runtime.CompilerServices;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using System.Management;
|
|||
|
using System.Runtime.CompilerServices;
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using System.Threading;
|
|||
|
namespace MasaBlazorApp3.Util
|
|||
|
{
|
|||
|
public class VirtualKeyboardHelper
|
|||
|
{
|
|||
|
private const uint WS_VISIBLE = 0x10000000;
|
|||
|
private const int GWL_STYLE = -16;
|
|||
|
private const int WM_SYSCOMMAND = 0x0112;
|
|||
|
private const uint SC_CLOSE = 0xF060;
|
|||
|
private const int WS_DISABLED = 0x08000000;
|
|||
|
private const int DWMWA_CLOAKED = 14;
|
|||
|
|
|||
|
private const string ApplicationFrameHostClassName = "ApplicationFrameWindow";
|
|||
|
private const string CoreWindowClassName = "Windows.UI.Core.CoreWindow";
|
|||
|
|
|||
|
private const string TextInputApplicationCaption = "Microsoft Text Input Application";
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// win10 虚拟键盘路径
|
|||
|
/// </summary>
|
|||
|
private const string Win10TabTipPath = @"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe";
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// win7 虚拟键盘路径
|
|||
|
/// </summary>
|
|||
|
private const string Win7OskPath = @"C:\WINDOWS\system32\osk.exe";
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 虚拟键盘 窗口名称
|
|||
|
/// </summary>
|
|||
|
private const string TabTipWindowClassName = "IPTIP_Main_Window";
|
|||
|
|
|||
|
|
|||
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|||
|
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
|||
|
|
|||
|
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
|
|||
|
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass,
|
|||
|
string lpszWindow);
|
|||
|
|
|||
|
|
|||
|
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
|
|||
|
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
|||
|
|
|||
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
|||
|
private static extern bool PostMessage(IntPtr hWnd, int msg, uint wParam, uint lParam);
|
|||
|
|
|||
|
|
|||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|||
|
private static extern bool Wow64DisableWow64FsRedirection(ref IntPtr ptr);
|
|||
|
|
|||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|||
|
private static extern bool Wow64RevertWow64FsRedirection(IntPtr ptr);
|
|||
|
|
|||
|
|
|||
|
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow", SetLastError = false)]
|
|||
|
private static extern IntPtr GetDesktopWindow();
|
|||
|
|
|||
|
[DllImport("dwmapi.dll", EntryPoint = "DwmGetWindowAttribute")]
|
|||
|
private static extern int DwmGetWindowAttribute(IntPtr intPtr, int dwAttribute, out int pvAttribute,
|
|||
|
uint cbAttribute);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 判断键盘是否连接
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
public static bool IsKeyboardAttached()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
ManagementObjectSearcher searcher =
|
|||
|
new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_Keyboard");
|
|||
|
|
|||
|
int devCount = 0;
|
|||
|
|
|||
|
foreach (ManagementObject obj in searcher.Get())
|
|||
|
{
|
|||
|
if (obj["Status"].ToString().Contains("OK")) // if device is ready
|
|||
|
{
|
|||
|
//surface测试时发现,HID设备,不是键盘,比较特殊
|
|||
|
if (!obj["Description"].ToString().Contains("HID Keyboard Device"))
|
|||
|
{
|
|||
|
devCount++;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return devCount > 0;
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 打开虚拟键盘,目前支持win7 64位,win10 64位,exe编译为x86。
|
|||
|
/// </summary>
|
|||
|
public static void ShowVirtualKeyboard()
|
|||
|
{
|
|||
|
//+------------------------------------------------------------------------------+
|
|||
|
//| | PlatformID | Major version | Minor version |
|
|||
|
//+------------------------------------------------------------------------------+
|
|||
|
//| Windows 95 | Win32Windows | 4 | 0 |
|
|||
|
//| Windows 98 | Win32Windows | 4 | 10 |
|
|||
|
//| Windows Me | Win32Windows | 4 | 90 |
|
|||
|
//| Windows NT 4.0 | Win32NT | 4 | 0 |
|
|||
|
//| Windows 2000 | Win32NT | 5 | 0 |
|
|||
|
//| Windows XP | Win32NT | 5 | 1 |
|
|||
|
//| Windows 2003 | Win32NT | 5 | 2 |
|
|||
|
//| Windows Vista | Win32NT | 6 | 0 |
|
|||
|
//| Windows 2008 | Win32NT | 6 | 0 |
|
|||
|
//| Windows 7 | Win32NT | 6 | 1 |
|
|||
|
//| Windows 2008 R2 | Win32NT | 6 | 1 |
|
|||
|
//| Windows 8 | Win32NT | 6 | 2 |
|
|||
|
//| Windows 8.1 | Win32NT | 6 | 3 |
|
|||
|
//+------------------------------------------------------------------------------+
|
|||
|
//| Windows 10 | Win32NT | 10 | 0 |
|
|||
|
|
|||
|
try
|
|||
|
{
|
|||
|
var isWin7 = Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor == 1;
|
|||
|
var isWin8OrWin10 =
|
|||
|
Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor == 2;
|
|||
|
var isWin10 = Environment.OSVersion.Version.Major == 10 && Environment.OSVersion.Version.Minor == 0;
|
|||
|
if (isWin7)
|
|||
|
{
|
|||
|
//win7
|
|||
|
ShowWin7VirtualKeyboard();
|
|||
|
}
|
|||
|
else if (isWin8OrWin10 || isWin10)
|
|||
|
{
|
|||
|
//win10
|
|||
|
ShowWin10VirtualKeyboard();
|
|||
|
}
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{
|
|||
|
// ignored
|
|||
|
}
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// 关闭虚拟键盘
|
|||
|
/// </summary>
|
|||
|
public void CloseVirtualKeyboard()
|
|||
|
{
|
|||
|
var touchhWnd = FindWindow("IPTip_Main_Window", null);
|
|||
|
if (touchhWnd == IntPtr.Zero)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PostMessage(touchhWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
|
|||
|
}
|
|||
|
private static void ShowWin7VirtualKeyboard()
|
|||
|
{
|
|||
|
if (!Environment.Is64BitProcess && Environment.Is64BitOperatingSystem)
|
|||
|
{
|
|||
|
//32位程序 运行在64位系统上,打开32位程序,需要禁用文件重定向
|
|||
|
var ptr = new IntPtr();
|
|||
|
var isWow64FsRedirectionDisabled = Wow64DisableWow64FsRedirection(ref ptr);
|
|||
|
|
|||
|
Process.Start(Win7OskPath);
|
|||
|
|
|||
|
if (isWow64FsRedirectionDisabled)
|
|||
|
{
|
|||
|
Wow64RevertWow64FsRedirection(ptr);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (Environment.Is64BitProcess && Environment.Is64BitOperatingSystem)
|
|||
|
{
|
|||
|
Process.Start(Win7OskPath);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static void ShowWin10VirtualKeyboard()
|
|||
|
{
|
|||
|
if (!IsTabTipProcessPresent())
|
|||
|
{
|
|||
|
|
|||
|
ProcessStartInfo startInfo = new ProcessStartInfo();
|
|||
|
startInfo.FileName = Win10TabTipPath;
|
|||
|
startInfo.UseShellExecute = true;
|
|||
|
startInfo.Verb = "runas";
|
|||
|
Process.Start(startInfo);
|
|||
|
while (!IsValidHandle(FindWindow("IPTIP_Main_Window", "")))
|
|||
|
{
|
|||
|
Thread.Sleep(100);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//判断可见性
|
|||
|
if (!IsWin10OnScreenKeyboardVisible())
|
|||
|
{
|
|||
|
ShowByCom();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static bool IsWin10OnScreenKeyboardVisible()
|
|||
|
{
|
|||
|
var handle = FindWindow(TabTipWindowClassName, "");
|
|||
|
if (!IsValidHandle(handle))
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
var isVisible = IsWindowVisibleByHandle(handle);
|
|||
|
if (isVisible.HasValue)
|
|||
|
{
|
|||
|
return isVisible.Value;
|
|||
|
}
|
|||
|
|
|||
|
// hard way
|
|||
|
var textInputHandle = FindTextInputWindow();
|
|||
|
return IsValidHandle(textInputHandle);
|
|||
|
}
|
|||
|
|
|||
|
private static IntPtr FindTextInputWindow()
|
|||
|
{
|
|||
|
var lastProbed = IntPtr.Zero;
|
|||
|
do
|
|||
|
{
|
|||
|
lastProbed = FindWindowEx(IntPtr.Zero, lastProbed, ApplicationFrameHostClassName, null);
|
|||
|
if (IsValidHandle(lastProbed))
|
|||
|
{
|
|||
|
var textInput = FindWindowEx(lastProbed, IntPtr.Zero, CoreWindowClassName,
|
|||
|
TextInputApplicationCaption);
|
|||
|
return textInput;
|
|||
|
}
|
|||
|
} while (IsValidHandle(lastProbed));
|
|||
|
|
|||
|
return IntPtr.Zero;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
private static bool? IsWindowVisibleByHandle(IntPtr handle)
|
|||
|
{
|
|||
|
var style = GetWindowLong(handle, GWL_STYLE);
|
|||
|
//Console.WriteLine( "Style {0:X8}", style );
|
|||
|
|
|||
|
// if is disabled - not visible
|
|||
|
if ((style & WS_DISABLED) != 0)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
// if has visible style - visible :)
|
|||
|
if ((style & WS_VISIBLE) != 0)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// DWM Window can be cloaked
|
|||
|
// see https://social.msdn.microsoft.com/Forums/vstudio/en-US/f8341376-6015-4796-8273-31e0be91da62/difference-between-actually-visible-and-not-visiblewhich-are-there-but-we-cant-see-windows-of?forum=vcgeneral
|
|||
|
if (DwmGetWindowAttribute(handle, DWMWA_CLOAKED, out var cloaked, 4) == 0)
|
|||
|
{
|
|||
|
if (cloaked != 0)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// undefined
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|||
|
private static bool IsValidHandle(IntPtr handle)
|
|||
|
{
|
|||
|
// if (?:) will be eliminated by jit
|
|||
|
return IntPtr.Size == 4
|
|||
|
? handle.ToInt32() > 0
|
|||
|
: handle.ToInt64() > 0;
|
|||
|
}
|
|||
|
|
|||
|
private static bool IsTabTipProcessPresent()
|
|||
|
{
|
|||
|
var handle = FindWindow(TabTipWindowClassName, "");
|
|||
|
return IntPtr.Size == 4
|
|||
|
? handle.ToInt32() > 0
|
|||
|
: handle.ToInt64() > 0;
|
|||
|
}
|
|||
|
|
|||
|
private static void ShowByCom()
|
|||
|
{
|
|||
|
ITipInvocation instance = null;
|
|||
|
try
|
|||
|
{
|
|||
|
instance = (ITipInvocation)Activator.CreateInstance(ComTypes.TipInvocationType);
|
|||
|
instance.Toggle(GetDesktopWindow());
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
if (!ReferenceEquals(instance, null))
|
|||
|
{
|
|||
|
Marshal.ReleaseComObject(instance);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[ComImport]
|
|||
|
[Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
|
|||
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
|||
|
internal interface ITipInvocation
|
|||
|
{
|
|||
|
void Toggle(IntPtr hwnd);
|
|||
|
}
|
|||
|
|
|||
|
internal static class ComTypes
|
|||
|
{
|
|||
|
internal static readonly Guid ImmersiveShellBrokerGuid;
|
|||
|
internal static readonly Type ImmersiveShellBrokerType;
|
|||
|
|
|||
|
internal static readonly Guid TipInvocationGuid;
|
|||
|
internal static readonly Type TipInvocationType;
|
|||
|
|
|||
|
static ComTypes()
|
|||
|
{
|
|||
|
TipInvocationGuid = Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376");
|
|||
|
TipInvocationType = Type.GetTypeFromCLSID(TipInvocationGuid);
|
|||
|
|
|||
|
ImmersiveShellBrokerGuid = new Guid("228826af-02e1-4226-a9e0-99a855e455a6");
|
|||
|
ImmersiveShellBrokerType = Type.GetTypeFromCLSID(ImmersiveShellBrokerGuid);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|