Files
desktop/WirelessTextSyncer.Windows/Services/KeyboardInjectionService.cs
2026-05-15 22:09:31 +08:00

171 lines
4.3 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 VirtualKeyA = 0x41;
private const ushort VirtualKeyBack = 0x08;
private const ushort VirtualKeyControl = 0x11;
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 ReplaceFocusedText(string text)
{
AppLogger.Info($"Replacing focused text length: {text.Length}");
SendModifiedKey(VirtualKeyControl, VirtualKeyA);
Backspace();
InsertText(text);
AppLogger.Info("Finished replacing focused text.");
}
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 SendModifiedKey(ushort modifierKey, ushort key)
{
Send([
CreateVirtualKeyInput(modifierKey, 0),
CreateVirtualKeyInput(key, 0),
CreateVirtualKeyInput(key, KeyEventFKeyUp),
CreateVirtualKeyInput(modifierKey, 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 MOUSEINPUT mi;
[FieldOffset(0)]
public KEYBDINPUT ki;
[FieldOffset(0)]
public HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct HARDWAREINPUT
{
public uint uMsg;
public ushort wParamL;
public ushort wParamH;
}
}