This commit is contained in:
2025-08-02 20:18:00 +03:00
parent 1e8a0d8756
commit 191678c8b2
12 changed files with 1548 additions and 6 deletions
+332
View File
@@ -0,0 +1,332 @@
/* This software is licensed by the MIT License, see LICENSE file */
/* Copyright © 2024-2025 Gregory Lirent */
#if WINIPC_UA_SERVER
using System.Runtime.InteropServices;
using WinIPC.Models;
namespace WinIPC.Utils
{
public class InputHandler
{
private Dictionary<IntPtr, Dictionary<Button, bool>> _state = new();
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, IntPtr pInputs, int cbSize);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtKey);
[DllImport("user32.dll")]
private static extern int MapVirtualKey(int uCode, uint uMapType);
private bool IsKeyPressed(int keyCode)
{
return (GetKeyState(keyCode) & 0x8000) != 0;
}
private 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;
}
[StructLayout(LayoutKind.Sequential)]
private struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
private struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(8)]
public MOUSEINPUT mi;
[FieldOffset(8)]
public KEYBDINPUT ki;
}
private static IntPtr MakeParam(int low, int high = 0)
{
return (high << 16) | (low & 0xffff);
}
private static IntPtr GetCursorPos(IntPtr hwnd)
{
WindowScanner.GetCursorPos(out var pos);
if (hwnd != IntPtr.Zero)
{
WindowScanner.ScreenToClient(hwnd, ref pos);
}
return MakeParam(pos.X, pos.Y);
}
private static readonly int INPUT_SIZE = Marshal.SizeOf(typeof(INPUT));
private Dictionary<Button, bool> GetStateDict(IntPtr hwnd)
{
Dictionary<Button, bool> state;
if (_state.TryGetValue(hwnd, out var s))
{
state = s;
}
else { _state.Add(hwnd, state = new()); }
return state;
}
private async Task<bool> ProcessMouseInput(IntPtr hwnd, Button button, bool press)
{
bool success = false;
var state = GetStateDict(hwnd);
if (state.TryGetValue(button, out var s) && s == press) return true;
return await Task.Run(async () =>
{
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var input = new INPUT
{
type = 0x00,
mi = new()
};
if (button.HasFlag(Button.MouseExtraFlag))
{
input.mi.dwFlags = (uint)Button.MouseExtraFlag << (press ? 0 : 1);
input.mi.mouseData = (int)(button & ~Button.MouseExtraFlag);
}
else
{
input.mi.dwFlags = (uint)button << (press ? 0 : 1);
}
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
int msg = 0;
int wFlags = 0;
switch(button)
{
case Button.MouseLeft:
msg = press ? 0x0201 : 0x0202;
break;
case Button.MouseRight:
msg = press ? 0x0204 : 0x0205;
break;
case Button.MouseMiddle:
msg = press ? 0x0207 : 0x0208;
break;
case Button.MouseExtra1:
case Button.MouseExtra2:
msg = press ? 0x020b : 0x020c;
wFlags = (int)button & 0x7f;
break;
default:
// TODO Log
break;
}
SendMessage(hwnd, msg, MakeParam(GetMouseKeyStates(), wFlags), GetCursorPos(hwnd));
success = true;
}
if (success) state[button] = press;
return success;
});
}
private async Task<bool> ProcessKeyboardInput(IntPtr hwnd, Button button, bool press)
{
bool success = false;
var state = GetStateDict(hwnd);
if (state.TryGetValue(button, out var s) && s == press) return true;
return await Task.Run<bool>(async () =>
{
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var flags = (uint)(press ? 0x00 : 0x02);
var input = new INPUT
{
type = 0x01,
ki = new()
};
if (button.HasFlag(Button.ExtendedKeyFlag))
{
flags |= 0x01;
button &= ~Button.ExtendedKeyFlag;
}
input.ki.wVk = (ushort)button;
input.ki.dwFlags = flags;
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
var btn = (int)(button & ~Button.ExtendedKeyFlag);
var msg = press ? 0x0100 : 0x0101;
if (button == Button.LAlt || button == Button.RAlt) msg += 4;
int flags = MapVirtualKey(btn, 0) | (button.HasFlag(Button.ExtendedKeyFlag) ? 0x0100 : 0) |
(IsKeyPressed(0x12) ? 0x2000 : 0) | (s ? 0x4000 : 0) | (press ? 0 : 0x8000);
SendMessage(hwnd, msg, MakeParam(btn), MakeParam(1, flags));
success = true;
}
if (success) state[button] = press;
return success;
});
}
public async Task<bool> KeyUp(IntPtr hwnd, Button button, int delay)
{
Task<bool> pTask;
bool success;
if (button.HasFlag(Button.MouseKeyFlag))
{
pTask = ProcessMouseInput(hwnd, button & ~Button.MouseKeyFlag, false);
}
else
{
pTask = ProcessKeyboardInput(hwnd, button, false);
}
if (success = await pTask)
{
await Task.Delay(delay);
}
return success;
}
public async Task<bool> KeyDown(IntPtr hwnd, Button button, int delay)
{
Task<bool> pTask;
bool success;
if (button.HasFlag(Button.MouseKeyFlag))
{
pTask = ProcessMouseInput(hwnd, button & ~Button.MouseKeyFlag, true);
}
else
{
pTask = ProcessKeyboardInput(hwnd, button, true);
}
if (success = await pTask)
{
await Task.Delay(delay);
}
return success;
}
public async Task<bool> MouseScroll(IntPtr hwnd, int offset, int delay)
{
bool success = false;
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var input = new INPUT
{
type = 0x00,
mi = new MOUSEINPUT
{
dwFlags = 0x0800,
mouseData = offset
}
};
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
SendMessage(hwnd, 0x020a, MakeParam(GetMouseKeyStates(), offset), GetCursorPos(IntPtr.Zero));
success = true;
}
await Task.Delay(delay);
return success;
}
public async Task<bool> MouseMove(IntPtr hwnd, int x, int y, int delay)
{
bool success = false;
if (hwnd == IntPtr.Zero)
{
var buffer = Marshal.AllocHGlobal(INPUT_SIZE);
var input = new INPUT
{
type = 0x00,
mi = new MOUSEINPUT
{
dwFlags = 0x8001,
dx = x,
dy = y
}
};
Marshal.StructureToPtr(input, buffer, true);
success = await Task.Run(() => { return 1 != SendInput(1, buffer, INPUT_SIZE); });
Marshal.FreeHGlobal(buffer);
}
else
{
SendMessage(hwnd, 0x0200, MakeParam(GetMouseKeyStates()), MakeParam(x, y));
success = true;
}
await Task.Delay(delay);
return success;
}
}
}
#endif
+100
View File
@@ -0,0 +1,100 @@
/* This software is licensed by the MIT License, see LICENSE file */
/* Copyright © 2024-2025 Gregory Lirent */
using Google.Protobuf;
using System.Reflection;
using WinIPC.Models;
namespace WinIPC.Utils
{
public static class PayloadHandler
{
public static byte[] Serialize<T>(T message) where T : IMessage<T>
{
if (message == null)
{
throw new ArgumentNullException(nameof(message), "Protobuf message cannot be null for serialization.");
}
return message.ToByteArray();
}
public static T Deserialize<T>(byte[] data) where T : IMessage<T>, new()
{
if (data == null)
{
throw new ArgumentNullException(nameof(data), "Byte array cannot be null for deserialization.");
}
PropertyInfo? parserProperty = typeof(T).GetProperty("Parser", BindingFlags.Static | BindingFlags.Public);
if (parserProperty == null)
{
throw new InvalidOperationException($"Type {typeof(T).Name} does not have a static 'Parser' property.");
}
MessageParser<T>? parser = parserProperty.GetValue(null) as MessageParser<T>;
if (parser == null)
{
throw new InvalidOperationException($"Could not get MessageParser for type {typeof(T).Name}.");
}
return parser.ParseFrom(data);
}
public static Request CreateRequest<T>(uint id, CommandType commandType, T payloadMessage) where T : IMessage<T>
{
return new Request
{
Id = id,
Type = commandType,
Payload = ByteString.CopyFrom(Serialize(payloadMessage))
};
}
public static Response CreateResponse<T>(uint id, bool success, string? errorMessage, T payloadMessage) where T : IMessage<T>
{
return new Response
{
Id = id,
Success = success,
Message = errorMessage ?? string.Empty,
Payload = ByteString.CopyFrom(Serialize(payloadMessage))
};
}
public static Response CreateErrorResponse(uint id, string errorMessage)
{
return new Response
{
Id = id,
Success = false,
Message = errorMessage
};
}
public static T ExtractPayload<T>(Request request) where T : IMessage<T>, new()
{
if (request == null)
{
throw new ArgumentNullException(nameof(request), "Request cannot be null.");
}
if (request.Payload == null || request.Payload.IsEmpty)
{
throw new ArgumentNullException(nameof(request.Payload), "Request payload is null or empty.");
}
return Deserialize<T>(request.Payload.ToByteArray());
}
public static T ExtractPayload<T>(Response response) where T : IMessage<T>, new()
{
if (response == null)
{
throw new ArgumentNullException(nameof(response), "Response cannot be null.");
}
if (response.Payload == null || response.Payload.IsEmpty)
{
throw new ArgumentNullException(nameof(response.Payload), "Response payload is null or empty.");
}
return Deserialize<T>(response.Payload.ToByteArray());
}
}
}
+133
View File
@@ -0,0 +1,133 @@
/* This software is licensed by the MIT License, see LICENSE file */
/* Copyright © 2024-2025 Gregory Lirent */
#if WINIPC_UA_SERVER
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using WinIPC.Models;
namespace WinIPC.Utils
{
public class WindowScanner
{
[StructLayout(LayoutKind.Explicit)]
internal struct RECT
{
[FieldOffset(0)]
public int Left;
[FieldOffset(4)]
public int Top;
[FieldOffset(8)]
public int Right;
[FieldOffset(12)]
public int Bottom;
[FieldOffset(0)]
public int X;
[FieldOffset(4)]
public int Y;
}
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(out RECT lpPoint);
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool ScreenToClient(IntPtr hWnd, ref RECT lpPoint);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
public static extern long GetWindowLongPtr(IntPtr hWnd, int nIndex);
private bool GetWindowInfo(ref WindowInfo data, IntPtr hwnd)
{
int length = GetWindowTextLength(hwnd);
if (length > 0)
{
var sb = new StringBuilder(length + 1);
GetWindowText(hwnd, sb, sb.Capacity);
data.Title = sb.ToString();
if (GetCursorPos(out RECT pt) && ScreenToClient(hwnd, ref pt))
{
data.CursorPosition = new Models.Point { X = pt.X, Y = pt.Y };
}
if (GetWindowRect(hwnd, out RECT wRect))
{
var rect = Rectangle.FromLTRB(wRect.Left, wRect.Top, wRect.Right, wRect.Bottom);
data.WindowPosition = new Models.Point { X = rect.X, Y = rect.Y };
data.WindowSize = new Models.AreaSize { Width = rect.Width, Height = rect.Height };
}
if (GetClientRect(hwnd, out RECT cRect))
{
var rect = Rectangle.FromLTRB(cRect.Left, cRect.Top, cRect.Right, cRect.Bottom);
data.ContentSize = new Models.AreaSize { Width = rect.Width, Height = rect.Height };
}
return true;
}
return false;
}
public async Task<IEnumerable<WindowInfo>> ScanAsync(ushort max = 0)
{
if (max == 0) max = UInt16.MaxValue;
return await Task.Run(() =>
{
var foreground = GetForegroundWindow();
List<WindowInfo> container = new();
EnumWindows(delegate (IntPtr hwnd, IntPtr lParam)
{
if (container.Count >= max) return false;
if (IsWindowVisible(hwnd))
{
WindowInfo data = new();
data.Hwnd = hwnd.ToInt32();
if ((data.ThreadId = (int)GetWindowThreadProcessId(hwnd, out uint pid)) != 0)
{
data.Pid = (int)pid;
if (GetWindowInfo(ref data, hwnd))
{
data.IsActive = foreground.ToInt32() == data.Hwnd;
container.Add(data);
}
}
}
return true;
}, IntPtr.Zero);
return container;
});
}
}
}
#endif