using System; using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; namespace ScreenExtender.Utility.Spy { public class SpyAgent { private readonly Action _selectedAction; private readonly Timer _timer; private SpyAgentLocator _locator; public SpyAgent(Action selectedAction) { _selectedAction = selectedAction; _timer = new Timer { Interval = 25, Enabled = false }; _timer.Tick += OnTimerTicked; BeginSpying(); } private void OnTimerTicked(object sender, EventArgs e) { ShowLocator(); if (0 == (Control.MouseButtons & MouseButtons.Left)) return; EndSpying(); _selectedAction(GetHoveredWindow()); } private void ShowLocator() { var window = GetHoveredWindow(); if (window.Handle == IntPtr.Zero) { _locator.Hide(); return; } _locator.Location = window.Area.Location; _locator.Size = window.Area.Size; _locator.TopLevel = true; _locator.TopMost = true; _locator.Show(); } public void BeginSpying() { if (_locator != null) { _locator.Close(); _locator.Dispose(); } _locator = new SpyAgentLocator(); MakePassThrough(_locator.Handle); _timer.Enabled = true; } public void EndSpying() { _timer.Enabled = false; _locator?.Close(); _locator?.Dispose(); _locator = null; } private class SpyAgentLocator : Form { public SpyAgentLocator() { FormBorderStyle = FormBorderStyle.None; BackColor = Color.OrangeRed; Opacity = 0.25; TopLevel = true; TopMost = true; } } #region Under the Hood private const int GWL_EXSTYLE = -20; private const int WS_EX_TRANSPARENT = 0x20; [DllImport("user32.dll")] private static extern IntPtr WindowFromPoint(POINT point); [DllImport("user32.dll")] private static extern IntPtr ChildWindowFromPoint(IntPtr hWndParent, POINT point); [DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll", SetLastError = true)] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); public static SpiedWindow GetHoveredWindow() { var handle = WindowFromPoint(Cursor.Position); return new SpiedWindow(handle); } private static void MakePassThrough(IntPtr handle) { var exstyle = GetWindowLong(handle, GWL_EXSTYLE); exstyle |= WS_EX_TRANSPARENT; SetWindowLong(handle, GWL_EXSTYLE, exstyle); } [StructLayout(LayoutKind.Sequential)] private struct POINT { public readonly int X; public readonly int Y; public POINT(int x, int y) { X = x; Y = y; } public POINT(Point pt) : this(pt.X, pt.Y) { } public static implicit operator Point(POINT p) { return new(p.X, p.Y); } public static implicit operator POINT(Point p) { return new(p.X, p.Y); } } #endregion Under the Hood } }