win32-automation-agent/Domain/InputHandler.cs

253 lines
9.0 KiB
C#
Raw Normal View History

2026-01-08 21:33:01 +03:00
using AutoAgent.Domain.Exceptions;
namespace AutoAgent.Domain
{
public static class InputHandler
{
private static Dictionary<IntPtr, Dictionary<Models.Button, bool>> _state = new();
private static bool IsKeyPressed(int keyCode)
{
return (User32Dll.GetKeyState(keyCode) & 0x8000) != 0;
}
private static ushort GetMouseKeyStates()
{
ushort keyStates = 0;
if (IsKeyPressed(0x01)) keyStates |= 0x0001; // lbutton
if (IsKeyPressed(0x02)) keyStates |= 0x0002; // rbutton
if (IsKeyPressed(0x04)) keyStates |= 0x0010; // mbutton
if (IsKeyPressed(0x05)) keyStates |= 0x0020; // xbutton1
if (IsKeyPressed(0x06)) keyStates |= 0x0040; // xbutton2
if (IsKeyPressed(0x10)) keyStates |= 0x0004; // Shift
if (IsKeyPressed(0x11)) keyStates |= 0x0008; // Ctrl
return keyStates;
}
private static IntPtr MakeParam(int low, int high = 0)
{
return (high << 16) | (low & 0xffff);
}
private static IntPtr GetCursorPos(IntPtr hwnd)
{
User32Dll.GetCursorPos(out var pos);
if (hwnd != IntPtr.Zero)
{
User32Dll.ScreenToClient(hwnd, ref pos);
}
return MakeParam(pos.X, pos.Y);
}
private static Dictionary<Models.Button, bool> GetStateDict(IntPtr hwnd)
{
Dictionary<Models.Button, bool> state;
if (_state.TryGetValue(hwnd, out var s))
{
state = s;
}
else { _state.Add(hwnd, state = new()); }
return state;
}
private static bool SendInput(Models.MemoryBuffer buffer, User32Dll.INPUT input)
{
return User32Dll.SendInput(1, buffer.SetStruct(input), User32Dll.INPUT_SIZE) != 1;
}
private static bool SendInput(Models.MemoryBuffer buffer, User32Dll.MOUSEINPUT input)
{
return SendInput(buffer, new User32Dll.INPUT() { type = 0x00, mi = input });
}
private static bool SendInput(Models.MemoryBuffer buffer, User32Dll.KEYBDINPUT input)
{
return SendInput(buffer, new User32Dll.INPUT() { type = 0x01, ki = input });
}
private static Task ProcessMouseInput(IntPtr hwnd, Models.Button button, bool press)
{
return Task.Run(() =>
{
var state = GetStateDict(hwnd);
if (state.TryGetValue(button, out var s) && s == press) return;
if (hwnd == IntPtr.Zero)
{
using (var buffer = new Models.MemoryBuffer(User32Dll.INPUT_SIZE))
{
var input = new User32Dll.MOUSEINPUT();
if (button.HasFlag(Models.Button.MouseExtraFlag))
{
input.dwFlags = (uint)Models.Button.MouseExtraFlag << (press ? 0 : 1);
input.mouseData = (int)(button & ~Models.Button.MouseExtraFlag);
}
else
{
input.dwFlags = (uint)button << (press ? 0 : 1);
}
if (!SendInput(buffer, input))
{
throw new InputException("SendInput is not successful");
}
}
}
else
{
int msg = 0;
int wFlags = 0;
switch (button)
{
case Models.Button.MouseLeft:
msg = press ? 0x0201 : 0x0202;
break;
case Models.Button.MouseRight:
msg = press ? 0x0204 : 0x0205;
break;
case Models.Button.MouseMiddle:
msg = press ? 0x0207 : 0x0208;
break;
case Models.Button.MouseExtra1:
case Models.Button.MouseExtra2:
msg = press ? 0x020b : 0x020c;
wFlags = (int)button & 0x7f;
break;
default:
throw new UnexpectedInputButton(button);
}
User32Dll.SendMessage(hwnd, msg, MakeParam(GetMouseKeyStates(), wFlags), GetCursorPos(hwnd));
}
state[button] = press;
});
}
private static Task ProcessKeyboardInput(IntPtr hwnd, Models.Button button, bool press)
{
return Task.Run(() =>
{
var state = GetStateDict(hwnd);
if (state.TryGetValue(button, out var s) && s == press) return;
if (hwnd == IntPtr.Zero)
{
using (var buffer = new Models.MemoryBuffer(User32Dll.INPUT_SIZE))
{
var flags = (uint)(press ? 0x00 : 0x02);
var input = new User32Dll.KEYBDINPUT();
if (button.HasFlag(Models.Button.ExtendedKeyFlag))
{
flags |= 0x01;
button &= ~Models.Button.ExtendedKeyFlag;
}
input.wVk = (ushort)button;
input.dwFlags = flags;
if (!SendInput(buffer, input))
{
throw new InputException("SendInput is not successful");
}
}
}
else
{
var btn = (int)(button & ~Models.Button.ExtendedKeyFlag);
var msg = press ? 0x0100 : 0x0101;
if (button == Models.Button.LAlt || button == Models.Button.RAlt) msg += 4;
int flags = User32Dll.MapVirtualKey(btn, 0) | (button.HasFlag(Models.Button.ExtendedKeyFlag) ? 0x0100 : 0) |
(IsKeyPressed(0x12) ? 0x2000 : 0) | (s ? 0x4000 : 0) | (press ? 0 : 0x8000);
User32Dll.SendMessage(hwnd, msg, MakeParam(btn), MakeParam(1, flags));
}
state[button] = press;
});
}
public static async Task KeyUp(IntPtr hwnd, Models.Button button, int delay)
{
if (button.HasFlag(Models.Button.MouseKeyFlag))
{
await ProcessMouseInput(hwnd, button & ~Models.Button.MouseKeyFlag, false);
}
else
{
await ProcessKeyboardInput(hwnd, button, false);
}
await Task.Delay(delay);
}
public static async Task KeyDown(IntPtr hwnd, Models.Button button, int delay)
{
if (button.HasFlag(Models.Button.MouseKeyFlag))
{
await ProcessMouseInput(hwnd, button & ~Models.Button.MouseKeyFlag, true);
}
else
{
await ProcessKeyboardInput(hwnd, button, true);
}
await Task.Delay(delay);
}
public static Task MouseScroll(IntPtr hwnd, int offset, int delay)
{
return Task.Run(async () =>
{
if (hwnd == IntPtr.Zero)
{
using (var buffer = new Models.MemoryBuffer(User32Dll.INPUT_SIZE))
{
if (!SendInput(buffer, new User32Dll.MOUSEINPUT { dwFlags = 0x0800, mouseData = offset } ))
{
throw new InputException("SendInput is not successful");
}
}
}
else
{
User32Dll.SendMessage(hwnd, 0x020a, MakeParam(GetMouseKeyStates(), offset), GetCursorPos(IntPtr.Zero));
}
await Task.Delay(delay);
});
}
public static Task MouseMove(IntPtr hwnd, Models.Point pos, int delay)
{
return Task.Run(async () =>
{
if (hwnd == IntPtr.Zero)
{
using (var buffer = new Models.MemoryBuffer(User32Dll.INPUT_SIZE))
{
if (!SendInput(buffer, new User32Dll.MOUSEINPUT { dwFlags = 0x8001, dx = pos.X, dy = pos.Y }))
{
throw new InputException("SendInput is not successful");
}
}
}
else
{
User32Dll.SendMessage(hwnd, 0x0200, MakeParam(GetMouseKeyStates()), MakeParam(pos.X, pos.Y));
}
await Task.Delay(delay);
});
}
}
}