-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathKeyboardSimulator.cs
228 lines (202 loc) · 10.4 KB
/
KeyboardSimulator.cs
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
using System;
using System.Collections.Generic;
using System.Threading;
using XPloit.Windows.Api.Native;
namespace XPloit.Windows.Api
{
/// <summary>
/// Implements the <see cref="IKeyboardSimulator"/> interface by calling the an <see cref="IInputMessageDispatcher"/> to simulate Keyboard gestures.
/// </summary>
public class KeyboardSimulator : IKeyboardSimulator
{
private readonly IInputSimulator _inputSimulator;
/// <summary>
/// The instance of the <see cref="IInputMessageDispatcher"/> to use for dispatching <see cref="INPUT"/> messages.
/// </summary>
private readonly IInputMessageDispatcher _messageDispatcher;
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardSimulator"/> class using an instance of a <see cref="WindowsInputMessageDispatcher"/> for dispatching <see cref="INPUT"/> messages.
/// </summary>
/// <param name="inputSimulator">The <see cref="IInputSimulator"/> that owns this instance.</param>
public KeyboardSimulator(IInputSimulator inputSimulator)
{
if (inputSimulator == null) throw new ArgumentNullException("inputSimulator");
_inputSimulator = inputSimulator;
_messageDispatcher = new WindowsInputMessageDispatcher();
}
/// <summary>
/// Initializes a new instance of the <see cref="KeyboardSimulator"/> class using the specified <see cref="IInputMessageDispatcher"/> for dispatching <see cref="INPUT"/> messages.
/// </summary>
/// <param name="inputSimulator">The <see cref="IInputSimulator"/> that owns this instance.</param>
/// <param name="messageDispatcher">The <see cref="IInputMessageDispatcher"/> to use for dispatching <see cref="INPUT"/> messages.</param>
/// <exception cref="InvalidOperationException">If null is passed as the <paramref name="messageDispatcher"/>.</exception>
internal KeyboardSimulator(IInputSimulator inputSimulator, IInputMessageDispatcher messageDispatcher)
{
if (inputSimulator == null) throw new ArgumentNullException("inputSimulator");
if (messageDispatcher == null)
throw new InvalidOperationException(
string.Format("The {0} cannot operate with a null {1}. Please provide a valid {1} instance to use for dispatching {2} messages.",
typeof(KeyboardSimulator).Name, typeof(IInputMessageDispatcher).Name, typeof(INPUT).Name));
_inputSimulator = inputSimulator;
_messageDispatcher = messageDispatcher;
}
/// <summary>
/// Gets the <see cref="IMouseSimulator"/> instance for simulating Mouse input.
/// </summary>
/// <value>The <see cref="IMouseSimulator"/> instance.</value>
public IMouseSimulator Mouse { get { return _inputSimulator.Mouse; } }
private void ModifiersDown(InputBuilder builder, IEnumerable<VirtualKeyCode> modifierKeyCodes)
{
if (modifierKeyCodes == null) return;
foreach (var key in modifierKeyCodes) builder.AddKeyDown(key);
}
private void ModifiersUp(InputBuilder builder, IEnumerable<VirtualKeyCode> modifierKeyCodes)
{
if (modifierKeyCodes == null) return;
// Key up in reverse (I miss LINQ)
var stack = new Stack<VirtualKeyCode>(modifierKeyCodes);
while (stack.Count > 0) builder.AddKeyUp(stack.Pop());
}
private void KeysPress(InputBuilder builder, IEnumerable<VirtualKeyCode> keyCodes)
{
if (keyCodes == null) return;
foreach (var key in keyCodes) builder.AddKeyPress(key);
}
/// <summary>
/// Sends the list of <see cref="INPUT"/> messages using the <see cref="IInputMessageDispatcher"/> instance.
/// </summary>
/// <param name="inputList">The <see cref="System.Array"/> of <see cref="INPUT"/> messages to send.</param>
private void SendSimulatedInput(INPUT[] inputList)
{
_messageDispatcher.DispatchInput(inputList);
}
/// <summary>
/// Calls the Win32 SendInput method to simulate a KeyDown.
/// </summary>
/// <param name="keyCode">The <see cref="VirtualKeyCode"/> to press</param>
public IKeyboardSimulator KeyDown(VirtualKeyCode keyCode)
{
var inputList = new InputBuilder().AddKeyDown(keyCode).ToArray();
SendSimulatedInput(inputList);
return this;
}
/// <summary>
/// Calls the Win32 SendInput method to simulate a KeyUp.
/// </summary>
/// <param name="keyCode">The <see cref="VirtualKeyCode"/> to lift up</param>
public IKeyboardSimulator KeyUp(VirtualKeyCode keyCode)
{
var inputList = new InputBuilder().AddKeyUp(keyCode).ToArray();
SendSimulatedInput(inputList);
return this;
}
/// <summary>
/// Calls the Win32 SendInput method with a KeyDown and KeyUp message in the same input sequence in order to simulate a Key PRESS.
/// </summary>
/// <param name="keyCode">The <see cref="VirtualKeyCode"/> to press</param>
public IKeyboardSimulator KeyPress(VirtualKeyCode keyCode)
{
var inputList = new InputBuilder().AddKeyPress(keyCode).ToArray();
SendSimulatedInput(inputList);
return this;
}
/// <summary>
/// Simulates a key press for each of the specified key codes in the order they are specified.
/// </summary>
/// <param name="keyCodes"></param>
public IKeyboardSimulator KeyPress(params VirtualKeyCode[] keyCodes)
{
var builder = new InputBuilder();
KeysPress(builder, keyCodes);
SendSimulatedInput(builder.ToArray());
return this;
}
/// <summary>
/// Simulates a simple modified keystroke like CTRL-C where CTRL is the modifierKey and C is the key.
/// The flow is Modifier KeyDown, Key Press, Modifier KeyUp.
/// </summary>
/// <param name="modifierKeyCode">The modifier key</param>
/// <param name="keyCode">The key to simulate</param>
public IKeyboardSimulator ModifiedKeyStroke(VirtualKeyCode modifierKeyCode, VirtualKeyCode keyCode)
{
ModifiedKeyStroke(new[] { modifierKeyCode }, new[] { keyCode });
return this;
}
/// <summary>
/// Simulates a modified keystroke where there are multiple modifiers and one key like CTRL-ALT-C where CTRL and ALT are the modifierKeys and C is the key.
/// The flow is Modifiers KeyDown in order, Key Press, Modifiers KeyUp in reverse order.
/// </summary>
/// <param name="modifierKeyCodes">The list of modifier keys</param>
/// <param name="keyCode">The key to simulate</param>
public IKeyboardSimulator ModifiedKeyStroke(IEnumerable<VirtualKeyCode> modifierKeyCodes, VirtualKeyCode keyCode)
{
ModifiedKeyStroke(modifierKeyCodes, new[] {keyCode});
return this;
}
/// <summary>
/// Simulates a modified keystroke where there is one modifier and multiple keys like CTRL-K-C where CTRL is the modifierKey and K and C are the keys.
/// The flow is Modifier KeyDown, Keys Press in order, Modifier KeyUp.
/// </summary>
/// <param name="modifierKey">The modifier key</param>
/// <param name="keyCodes">The list of keys to simulate</param>
public IKeyboardSimulator ModifiedKeyStroke(VirtualKeyCode modifierKey, IEnumerable<VirtualKeyCode> keyCodes)
{
ModifiedKeyStroke(new [] {modifierKey}, keyCodes);
return this;
}
/// <summary>
/// Simulates a modified keystroke where there are multiple modifiers and multiple keys like CTRL-ALT-K-C where CTRL and ALT are the modifierKeys and K and C are the keys.
/// The flow is Modifiers KeyDown in order, Keys Press in order, Modifiers KeyUp in reverse order.
/// </summary>
/// <param name="modifierKeyCodes">The list of modifier keys</param>
/// <param name="keyCodes">The list of keys to simulate</param>
public IKeyboardSimulator ModifiedKeyStroke(IEnumerable<VirtualKeyCode> modifierKeyCodes, IEnumerable<VirtualKeyCode> keyCodes)
{
var builder = new InputBuilder();
ModifiersDown(builder, modifierKeyCodes);
KeysPress(builder, keyCodes);
ModifiersUp(builder, modifierKeyCodes);
SendSimulatedInput(builder.ToArray());
return this;
}
/// <summary>
/// Calls the Win32 SendInput method with a stream of KeyDown and KeyUp messages in order to simulate uninterrupted text entry via the keyboard.
/// </summary>
/// <param name="text">The text to be simulated.</param>
public IKeyboardSimulator TextEntry(string text)
{
if (text.Length > UInt32.MaxValue / 2) throw new ArgumentException(string.Format("The text parameter is too long. It must be less than {0} characters.", UInt32.MaxValue / 2), "text");
var inputList = new InputBuilder().AddCharacters(text).ToArray();
SendSimulatedInput(inputList);
return this;
}
/// <summary>
/// Simulates a single character text entry via the keyboard.
/// </summary>
/// <param name="character">The unicode character to be simulated.</param>
public IKeyboardSimulator TextEntry(char character)
{
var inputList = new InputBuilder().AddCharacter(character).ToArray();
SendSimulatedInput(inputList);
return this;
}
/// <summary>
/// Sleeps the executing thread to create a pause between simulated inputs.
/// </summary>
/// <param name="millsecondsTimeout">The number of milliseconds to wait.</param>
public IKeyboardSimulator Sleep(int millsecondsTimeout)
{
Thread.Sleep(millsecondsTimeout);
return this;
}
/// <summary>
/// Sleeps the executing thread to create a pause between simulated inputs.
/// </summary>
/// <param name="timeout">The time to wait.</param>
public IKeyboardSimulator Sleep(TimeSpan timeout)
{
Thread.Sleep(timeout);
return this;
}
}
}