using System; using System.Collections.Generic; using System.Threading; using WindowsInput.Native; namespace WindowsInput { /// /// Implements the interface by calling the an to simulate Keyboard gestures. /// public class KeyboardSimulator : IKeyboardSimulator { private readonly IInputSimulator _inputSimulator; /// /// The instance of the to use for dispatching messages. /// private readonly IInputMessageDispatcher _messageDispatcher; /// /// Initializes a new instance of the class using an instance of a for dispatching messages. /// /// The that owns this instance. public KeyboardSimulator(IInputSimulator inputSimulator) { if (inputSimulator == null) throw new ArgumentNullException("inputSimulator"); _inputSimulator = inputSimulator; _messageDispatcher = new WindowsInputMessageDispatcher(); } /// /// Initializes a new instance of the class using the specified for dispatching messages. /// /// The that owns this instance. /// The to use for dispatching messages. /// If null is passed as the . 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; } /// /// Gets the instance for simulating Mouse input. /// /// The instance. public IMouseSimulator Mouse { get { return _inputSimulator.Mouse; } } private void ModifiersDown(InputBuilder builder, IEnumerable modifierKeyCodes) { if (modifierKeyCodes == null) return; foreach (var key in modifierKeyCodes) builder.AddKeyDown(key); } private void ModifiersUp(InputBuilder builder, IEnumerable modifierKeyCodes) { if (modifierKeyCodes == null) return; // Key up in reverse (I miss LINQ) var stack = new Stack(modifierKeyCodes); while (stack.Count > 0) builder.AddKeyUp(stack.Pop()); } private void KeysPress(InputBuilder builder, IEnumerable keyCodes) { if (keyCodes == null) return; foreach (var key in keyCodes) builder.AddKeyPress(key); } /// /// Sends the list of messages using the instance. /// /// The of messages to send. private void SendSimulatedInput(INPUT[] inputList) { _messageDispatcher.DispatchInput(inputList); } /// /// Calls the Win32 SendInput method to simulate a KeyDown. /// /// The to press public IKeyboardSimulator KeyDown(VirtualKeyCode keyCode) { var inputList = new InputBuilder().AddKeyDown(keyCode).ToArray(); SendSimulatedInput(inputList); return this; } /// /// Calls the Win32 SendInput method to simulate a KeyUp. /// /// The to lift up public IKeyboardSimulator KeyUp(VirtualKeyCode keyCode) { var inputList = new InputBuilder().AddKeyUp(keyCode).ToArray(); SendSimulatedInput(inputList); return this; } /// /// Calls the Win32 SendInput method with a KeyDown and KeyUp message in the same input sequence in order to simulate a Key PRESS. /// /// The to press public IKeyboardSimulator KeyPress(VirtualKeyCode keyCode) { var inputList = new InputBuilder().AddKeyPress(keyCode).ToArray(); SendSimulatedInput(inputList); return this; } /// /// Simulates a key press for each of the specified key codes in the order they are specified. /// /// public IKeyboardSimulator KeyPress(params VirtualKeyCode[] keyCodes) { var builder = new InputBuilder(); KeysPress(builder, keyCodes); SendSimulatedInput(builder.ToArray()); return this; } /// /// 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. /// /// The modifier key /// The key to simulate public IKeyboardSimulator ModifiedKeyStroke(VirtualKeyCode modifierKeyCode, VirtualKeyCode keyCode) { ModifiedKeyStroke(new[] { modifierKeyCode }, new[] { keyCode }); return this; } /// /// 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. /// /// The list of modifier keys /// The key to simulate public IKeyboardSimulator ModifiedKeyStroke(IEnumerable modifierKeyCodes, VirtualKeyCode keyCode) { ModifiedKeyStroke(modifierKeyCodes, new[] {keyCode}); return this; } /// /// 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. /// /// The modifier key /// The list of keys to simulate public IKeyboardSimulator ModifiedKeyStroke(VirtualKeyCode modifierKey, IEnumerable keyCodes) { ModifiedKeyStroke(new [] {modifierKey}, keyCodes); return this; } /// /// 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. /// /// The list of modifier keys /// The list of keys to simulate public IKeyboardSimulator ModifiedKeyStroke(IEnumerable modifierKeyCodes, IEnumerable keyCodes) { var builder = new InputBuilder(); ModifiersDown(builder, modifierKeyCodes); KeysPress(builder, keyCodes); ModifiersUp(builder, modifierKeyCodes); SendSimulatedInput(builder.ToArray()); return this; } /// /// Calls the Win32 SendInput method with a stream of KeyDown and KeyUp messages in order to simulate uninterrupted text entry via the keyboard. /// /// The text to be simulated. 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; } /// /// Simulates a single character text entry via the keyboard. /// /// The unicode character to be simulated. public IKeyboardSimulator TextEntry(char character) { var inputList = new InputBuilder().AddCharacter(character).ToArray(); SendSimulatedInput(inputList); return this; } /// /// Sleeps the executing thread to create a pause between simulated inputs. /// /// The number of milliseconds to wait. public IKeyboardSimulator Sleep(int millsecondsTimeout) { Thread.Sleep(millsecondsTimeout); return this; } /// /// Sleeps the executing thread to create a pause between simulated inputs. /// /// The time to wait. public IKeyboardSimulator Sleep(TimeSpan timeout) { Thread.Sleep(timeout); return this; } } }