using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text; using ytLive.Models; namespace ytLive.Services; /// /// Win32 implementation of . Enumerates visible /// top-level windows for the "Capture Window…" submenu, mirroring the style of /// : visible, non-cloaked, zero-rect /// excluded, and windows belonging to this process excluded (the app must never /// offer itself for pinning). Ordered by process name, then title. /// public sealed class Win32WindowEnumerator : IWindowEnumerator { private static readonly IntPtr OwnProcessId = new(Environment.ProcessId); public IReadOnlyList Enumerate() { var results = new List(); EnumWindows((hwnd, _) => { if (TryDescribe(hwnd, out var info)) results.Add(info); return true; // keep enumerating }, IntPtr.Zero); results.Sort(static (a, b) => { var byProcess = string.Compare(a.ProcessName, b.ProcessName, StringComparison.OrdinalIgnoreCase); return byProcess != 0 ? byProcess : string.Compare(a.Title, b.Title, StringComparison.OrdinalIgnoreCase); }); return results; } private static bool TryDescribe(IntPtr hwnd, out WindowInfo info) { info = null!; if (GetWindowThreadProcessId(hwnd, out var pid) == 0) return false; if (pid == 0 || new IntPtr((long)pid) == OwnProcessId) return false; if (!IsWindowVisible(hwnd)) return false; if (IsCloaked(hwnd)) return false; if (!TryGetRect(hwnd, out var rect) || rect.Right <= rect.Left || rect.Bottom <= rect.Top) return false; var title = GetTitle(hwnd); if (string.IsNullOrWhiteSpace(title)) return false; var processName = ProcessName((int)pid); if (string.IsNullOrWhiteSpace(processName)) return false; info = new WindowInfo(title, processName, hwnd.ToInt64(), (int)pid); return true; } private static string ProcessName(int pid) { try { using var process = Process.GetProcessById(pid); return process.ProcessName; } catch { return string.Empty; } } private static string GetTitle(IntPtr hwnd) { var length = GetWindowTextLength(hwnd); if (length <= 0) return string.Empty; var sb = new StringBuilder(length + 1); GetWindowText(hwnd, sb, sb.Capacity); return sb.ToString(); } private static bool IsCloaked(IntPtr hwnd) { const int DWMWA_CLOAKED = 14; var cloaked = 0; return DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, out cloaked, sizeof(int)) == 0 && cloaked != 0; } private static bool TryGetRect(IntPtr hwnd, out Rect rect) { rect = default; return GetWindowRect(hwnd, out rect); } [StructLayout(LayoutKind.Sequential)] private struct Rect { public int Left; public int Top; public int Right; public int Bottom; } private delegate bool EnumWindowsProc(IntPtr hwnd, IntPtr lParam); [DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowTextLength(IntPtr hWnd); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, out Rect lpRect); [DllImport("dwmapi.dll")] private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out int pvAttribute, int cbAttribute); }