namespace WirelessTextSyncer.Windows.Services; using System.Runtime.InteropServices; public sealed class KeyboardInjectionService : IKeyboardInjectionService { private const uint InputKeyboard = 1; private const uint KeyEventFKeyUp = 0x0002; private const uint KeyEventFUnicode = 0x0004; private const ushort VirtualKeyBack = 0x08; private const ushort VirtualKeyReturn = 0x0D; public void InsertText(string text) { if (string.IsNullOrEmpty(text)) { return; } foreach (var character in text) { SendUnicode(character); } } public void Backspace() { SendVirtualKey(VirtualKeyBack); } public void Enter() { SendVirtualKey(VirtualKeyReturn); } private static void SendUnicode(char character) { Send([ CreateUnicodeInput(character, 0), CreateUnicodeInput(character, KeyEventFKeyUp) ]); } private static void SendVirtualKey(ushort virtualKey) { Send([ CreateVirtualKeyInput(virtualKey, 0), CreateVirtualKeyInput(virtualKey, KeyEventFKeyUp) ]); } private static void Send(INPUT[] inputs) { var sent = SendInput((uint)inputs.Length, inputs, Marshal.SizeOf()); if (sent != inputs.Length) { throw new InvalidOperationException("Windows did not accept all keyboard input events."); } } private static INPUT CreateUnicodeInput(char character, uint flags) { return new INPUT { type = InputKeyboard, U = new InputUnion { ki = new KEYBDINPUT { wScan = character, dwFlags = KeyEventFUnicode | flags } } }; } private static INPUT CreateVirtualKeyInput(ushort virtualKey, uint flags) { return new INPUT { type = InputKeyboard, U = new InputUnion { ki = new KEYBDINPUT { wVk = virtualKey, dwFlags = flags } } }; } [DllImport("user32.dll", SetLastError = true)] private static extern uint SendInput(uint numberOfInputs, INPUT[] inputs, int sizeOfInputStructure); [StructLayout(LayoutKind.Sequential)] private struct INPUT { public uint type; public InputUnion U; } [StructLayout(LayoutKind.Explicit)] private struct InputUnion { [FieldOffset(0)] public KEYBDINPUT ki; } [StructLayout(LayoutKind.Sequential)] private struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public UIntPtr dwExtraInfo; } }