Files
desktop/WirelessTextSyncer.Windows/Services/KeyboardInjectionService.cs

125 lines
3.1 KiB
C#

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;
}
AppLogger.Info($"Injecting text length: {text.Length}");
foreach (var character in text)
{
SendUnicode(character);
}
}
public void Backspace()
{
AppLogger.Info("Injecting backspace.");
SendVirtualKey(VirtualKeyBack);
}
public void Enter()
{
AppLogger.Info("Injecting 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<INPUT>());
if (sent != inputs.Length)
{
var error = Marshal.GetLastWin32Error();
AppLogger.Error(
$"Windows accepted {sent}/{inputs.Length} keyboard input events. Win32 error: {error}");
}
}
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;
}
}