using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using AxCopilot.SDK; namespace AxCopilot.Handlers; public class WindowSwitchHandler : IActionHandler { private delegate bool EnumWindowsProc(nint hWnd, nint lParam); private record WindowInfo(nint Handle, string Title, string ProcessName, bool IsMinimized); private const int SW_RESTORE = 9; public string? Prefix => "win"; public PluginMetadata Metadata => new PluginMetadata("WindowSwitch", "윈도우 포커스 스위처 — win", "1.0", "AX"); [DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, nint lParam); [DllImport("user32.dll")] private static extern bool IsWindowVisible(nint hWnd); [DllImport("user32.dll")] private static extern int GetWindowTextLength(nint hWnd); [DllImport("user32.dll")] private static extern int GetWindowText(nint hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(nint hWnd, out uint lpdwProcessId); [DllImport("user32.dll")] private static extern bool SetForegroundWindow(nint hWnd); [DllImport("user32.dll")] private static extern bool ShowWindow(nint hWnd, int nCmdShow); [DllImport("user32.dll")] private static extern bool IsIconic(nint hWnd); [DllImport("user32.dll")] private static extern nint GetShellWindow(); public Task> GetItemsAsync(string query, CancellationToken ct) { string q = query.Trim(); List list = GetOpenWindows(); if (!string.IsNullOrWhiteSpace(q)) { list = list.Where((WindowInfo w) => w.Title.Contains(q, StringComparison.OrdinalIgnoreCase) || w.ProcessName.Contains(q, StringComparison.OrdinalIgnoreCase)).ToList(); } if (!list.Any()) { return Task.FromResult((IEnumerable)new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem(string.IsNullOrWhiteSpace(q) ? "열린 창이 없습니다" : ("'" + q + "'에 해당하는 창 없음"), "win 뒤에 프로세스명 또는 창 제목 입력", null, null, null, "\ue946"))); } List list2 = (from w in list.Take(15) select new LauncherItem((w.Title.Length > 60) ? (w.Title.Substring(0, 57) + "…") : w.Title, w.ProcessName + " · " + (w.IsMinimized ? "[최소화됨] · " : "") + "Enter → 전환", null, w.Handle, null, "\ue7f4")).ToList(); list2.Insert(0, new LauncherItem($"열린 창 {list.Count}개", "Enter → 해당 창으로 즉시 전환", null, null, null, "\ue946")); return Task.FromResult((IEnumerable)list2); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.Data is nint num && num != IntPtr.Zero) { if (IsIconic(num)) { ShowWindow(num, 9); } SetForegroundWindow(num); } return Task.CompletedTask; } private static List GetOpenWindows() { List list = new List(); nint shellWnd = GetShellWindow(); string currentProcessName = Process.GetCurrentProcess().ProcessName; EnumWindows(delegate(nint hWnd, nint _) { if (hWnd == shellWnd) { return true; } if (!IsWindowVisible(hWnd)) { return true; } int windowTextLength = GetWindowTextLength(hWnd); if (windowTextLength == 0) { return true; } StringBuilder stringBuilder = new StringBuilder(windowTextLength + 1); GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity); string title = stringBuilder.ToString(); string text = ""; try { GetWindowThreadProcessId(hWnd, out var lpdwProcessId); text = Process.GetProcessById((int)lpdwProcessId).ProcessName; } catch { } if (text == currentProcessName) { return true; } bool flag; switch (text) { case "ApplicationFrameHost": case "ShellExperienceHost": case "SearchHost": case "StartMenuExperienceHost": case "TextInputHost": flag = true; break; default: flag = false; break; } if (flag) { return true; } list.Add(new WindowInfo(hWnd, title, text, IsIconic(hWnd))); return true; }, IntPtr.Zero); return list; } }