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);
}