using System.Drawing; using System.Runtime.InteropServices; using System.Text; using WebmrAPI.Models; using WebmrAPI.Utils; namespace WebmrAPI.Services.Scanners { public class WindowScanner : AbstractScanner { [StructLayout(LayoutKind.Explicit)] private 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)] private 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)] private 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 int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId); [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); override public ScanTarget Target { get => ScanTarget.Windows; } private bool GetWindowInfo(WindowInfo info, IntPtr hwnd) { int length = GetWindowTextLength(hwnd); if (length > 0) { var sb = new StringBuilder(length + 1); GetWindowText(hwnd, sb, sb.Capacity); info.Title = sb.ToString(); if (GetCursorPos(out RECT pt) && ScreenToClient(hwnd, ref pt)) { info.CursorPosition = new GeometryPoint(pt.X, pt.Y); } if (GetWindowRect(hwnd, out RECT wRect)) { info.WindowGeometry = Geometry.FromLTRB(wRect.Left, wRect.Top, wRect.Right, wRect.Bottom); } if (GetClientRect(hwnd, out RECT cRect)) { info.ContentGeometry = BaseGeometry.FromLTRB(cRect.Left, cRect.Top, cRect.Right, cRect.Bottom); } return true; } return false; } override internal Task ScanAsync(Dictionary data) { return Task.Run(() => { var foreground = GetForegroundWindow(); EnumWindows(delegate (IntPtr hwnd, IntPtr lParam) { if (IsWindowVisible(hwnd)) { WindowInfo? info; if (!GetFromCacheOrNew(hwnd.ToInt64(), out info)) { info.Hwnd = hwnd; int threadId; if ((threadId = GetWindowThreadProcessId(hwnd, out int pid)) != 0) { info.PID = pid; info.ThreadId = threadId; } else { info = null; } } if (info != null && GetWindowInfo(info, hwnd)) { info.IsActive = foreground == info.Hwnd; data.Add(hwnd.ToInt64(), info); } } return true; }, IntPtr.Zero); return true; }); } public WindowScanner(IScanProvider scanner, LazyConcurrentContainer container) : base(scanner, container) { } } }