Repository navigation
Expand file tree
/
Copy pathMaestroKeyboard.cs
More file actions
132 lines (120 loc) · 5.53 KB
/
Copy pathMaestroKeyboard.cs
File metadata and controls
132 lines (120 loc) · 5.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.Win32;
namespace KeyParcel
{
internal sealed class MaestroKeyboard : IVirtualKeyboard
{
private readonly string executable;
private NamedPipeClientStream connection;
private bool disposed;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetNamedPipeServerProcessId(Microsoft.Win32.SafeHandles.SafePipeHandle pipe, out uint process);
private MaestroKeyboard(string executable) { this.executable = executable; }
internal static MaestroKeyboard Create()
{
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "keyboard", "KeyParcel.Keyboard.exe");
return File.Exists(path) ? new MaestroKeyboard(path) : null;
}
public bool IsReady { get { return connection != null && connection.IsConnected; } }
public bool NeedsInstallConfirmation
{
get
{
try
{
using (var driver = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\WUDF\Services\HIDMaestro"))
return driver == null;
}
catch (System.Security.SecurityException) { return true; }
catch (UnauthorizedAccessException) { return true; }
catch (IOException) { return true; }
}
}
public async Task<string> Start()
{
try
{
NamedPipeClientStream pipe = await Task.Run(() => Connect());
if (disposed) { pipe.Dispose(); return "虚拟键盘已关闭。"; }
Close();
connection = pipe;
return null;
}
catch (Win32Exception error)
{
return error.NativeErrorCode == 1223 ? "已取消管理员授权,仍使用模拟输入。" : "无法启动虚拟键盘助手。";
}
catch (IOException) { return "虚拟键盘未就绪,请检查驱动安装。"; }
catch (TimeoutException) { return "虚拟键盘启动超时,请重试。"; }
catch (UnauthorizedAccessException) { return "无法连接虚拟键盘助手,请使用当前账号授权。"; }
}
private NamedPipeClientStream Connect()
{
string nonce = Guid.NewGuid().ToString("N");
int caller;
using (var current = Process.GetCurrentProcess()) caller = current.Id;
var launch = new ProcessStartInfo(executable, nonce + " " + caller)
{
UseShellExecute = true, Verb = "runas", WorkingDirectory = Path.GetDirectoryName(executable),
WindowStyle = ProcessWindowStyle.Hidden
};
using (var helper = Process.Start(launch))
{
if (helper == null) throw new IOException("Keyboard helper did not start.");
var pipe = new NamedPipeClientStream(".", "KeyParcel.Keyboard." + nonce, PipeDirection.InOut,
PipeOptions.Asynchronous, TokenImpersonationLevel.Identification);
try
{
pipe.Connect(10000);
uint server;
if (!GetNamedPipeServerProcessId(pipe.SafePipeHandle, out server) || server != (uint)helper.Id)
throw new IOException("Keyboard helper identity did not match.");
if (ReadReply(pipe, 90000) != 1) throw new IOException("Keyboard helper failed to create a device.");
return pipe;
}
catch { pipe.Dispose(); throw; }
}
}
private static int ReadReply(NamedPipeClientStream pipe, int timeout)
{
byte[] reply = new byte[1];
Task<int> read = pipe.ReadAsync(reply, 0, 1);
if (Task.WhenAny(read, Task.Delay(timeout)).GetAwaiter().GetResult() != read) throw new TimeoutException();
return read.GetAwaiter().GetResult() == 1 ? reply[0] : -1;
}
public void Send(IntPtr window, string unit)
{
if (!IsReady) throw new InvalidOperationException("虚拟键盘已断开,请重新切换输入方式。");
byte[] request = new byte[10];
Array.Copy(BitConverter.GetBytes(window.ToInt64()), request, 8);
Array.Copy(BitConverter.GetBytes((ushort)unit[0]), 0, request, 8, 2);
int result;
try
{
connection.Write(request, 0, request.Length);
result = ReadReply(connection, 2000);
}
catch (IOException) { result = -1; }
catch (TimeoutException) { result = -1; }
if (result == 1) return;
if (result == 2) throw new InvalidOperationException("请在目标窗口按 Win + 空格,选择美式键盘。");
if (result == 3) throw new InvalidOperationException("请松开 Ctrl、Alt、Shift 和 Win 键再试。");
if (result < 0) Close();
throw new InvalidOperationException("虚拟键盘无法向当前窗口输入。");
}
public void Close()
{
if (connection == null) return;
connection.Dispose();
connection = null;
}
public void Dispose() { disposed = true; Close(); }
}
}