Files
AX-Copilot-Codex/.decompiledproj/AxCopilot/Core/ContextManager.cs

385 lines
9.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AxCopilot.Models;
using AxCopilot.Services;
namespace AxCopilot.Core;
public class ContextManager
{
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private struct WINDOWPLACEMENT
{
public uint length;
public uint flags;
public uint showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}
private struct POINT
{
public int x;
public int y;
}
private delegate bool EnumWindowsProc(nint hWnd, nint lParam);
private delegate bool MonitorEnumProc(nint hMonitor, nint hdcMonitor, ref RECT lprcMonitor, nint dwData);
private readonly SettingsService _settings;
private const uint SWP_NOZORDER = 4u;
private const uint SWP_NOACTIVATE = 16u;
private const uint MONITOR_DEFAULTTONEAREST = 2u;
public ContextManager(SettingsService settings)
{
_settings = settings;
}
public WorkspaceProfile CaptureProfile(string name)
{
List<WindowSnapshot> snapshots = new List<WindowSnapshot>();
Dictionary<nint, int> monitorMap = BuildMonitorMap();
EnumWindows(delegate(nint hWnd, nint _)
{
if (!IsWindowVisible(hWnd))
{
return true;
}
if (IsIconic(hWnd))
{
return true;
}
string windowTitle = GetWindowTitle(hWnd);
if (string.IsNullOrWhiteSpace(windowTitle))
{
return true;
}
if (IsSystemWindow(hWnd))
{
return true;
}
string processPath = GetProcessPath(hWnd);
if (string.IsNullOrEmpty(processPath))
{
return true;
}
GetWindowRect(hWnd, out var lpRect);
GetWindowPlacement(hWnd, out var lpwndpl);
uint showCmd = lpwndpl.showCmd;
if (1 == 0)
{
}
string text = showCmd switch
{
1u => "Normal",
2u => "Minimized",
3u => "Maximized",
_ => "Normal",
};
if (1 == 0)
{
}
string showCmd2 = text;
int monitorIndex = GetMonitorIndex(hWnd, monitorMap);
snapshots.Add(new WindowSnapshot
{
Exe = processPath,
Title = windowTitle,
Rect = new WindowRect
{
X = lpRect.Left,
Y = lpRect.Top,
Width = lpRect.Right - lpRect.Left,
Height = lpRect.Bottom - lpRect.Top
},
ShowCmd = showCmd2,
Monitor = monitorIndex
});
return true;
}, IntPtr.Zero);
WorkspaceProfile workspaceProfile = new WorkspaceProfile
{
Name = name,
Windows = snapshots,
CreatedAt = DateTime.Now
};
WorkspaceProfile workspaceProfile2 = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (workspaceProfile2 != null)
{
_settings.Settings.Profiles.Remove(workspaceProfile2);
}
_settings.Settings.Profiles.Add(workspaceProfile);
_settings.Save();
LogService.Info($"프로필 '{name}' 저장 완료: {snapshots.Count}개 창");
return workspaceProfile;
}
public async Task<RestoreResult> RestoreProfileAsync(string name, CancellationToken ct = default(CancellationToken))
{
WorkspaceProfile profile = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (profile == null)
{
return new RestoreResult(Success: false, "프로필 '" + name + "'을 찾을 수 없습니다.");
}
List<string> results = new List<string>();
int monitorCount = GetMonitorCount();
foreach (WindowSnapshot snapshot in profile.Windows)
{
ct.ThrowIfCancellationRequested();
nint hWnd = FindMatchingWindow(snapshot);
if (hWnd == IntPtr.Zero && File.Exists(snapshot.Exe))
{
try
{
Process.Start(new ProcessStartInfo(snapshot.Exe)
{
UseShellExecute = true
});
hWnd = await WaitForWindowAsync(snapshot.Exe, TimeSpan.FromSeconds(3.0), ct);
}
catch (Exception ex)
{
Exception ex2 = ex;
results.Add($"⚠ {snapshot.Title}: 실행 실패 ({ex2.Message})");
LogService.Warn("앱 실행 실패: " + snapshot.Exe + " - " + ex2.Message);
continue;
}
}
if (hWnd == IntPtr.Zero)
{
results.Add("⏭ " + snapshot.Title + ": 창 없음, 건너뜀");
continue;
}
if (snapshot.Monitor >= monitorCount)
{
string policy = _settings.Settings.MonitorMismatch;
if (policy == "skip")
{
results.Add("⏭ " + snapshot.Title + ": 모니터 불일치, 건너뜀");
continue;
}
}
try
{
nint hWnd2 = hWnd;
string showCmd = snapshot.ShowCmd;
if (1 == 0)
{
}
string text = showCmd;
int nCmdShow = ((text == "Maximized") ? 3 : ((!(text == "Minimized")) ? 9 : 2));
if (1 == 0)
{
}
ShowWindow(hWnd2, nCmdShow);
if (snapshot.ShowCmd == "Normal")
{
SetWindowPos(hWnd, IntPtr.Zero, snapshot.Rect.X, snapshot.Rect.Y, snapshot.Rect.Width, snapshot.Rect.Height, 20u);
}
results.Add("✓ " + snapshot.Title + ": 복원 완료");
}
catch (Exception ex3)
{
results.Add($"⚠ {snapshot.Title}: 복원 실패 ({ex3.Message})");
LogService.Warn("창 복원 실패 (권한 문제 가능): " + snapshot.Exe);
}
}
LogService.Info("프로필 '" + name + "' 복원: " + string.Join(", ", results));
return new RestoreResult(Success: true, string.Join("\n", results));
}
public bool DeleteProfile(string name)
{
WorkspaceProfile workspaceProfile = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (workspaceProfile == null)
{
return false;
}
_settings.Settings.Profiles.Remove(workspaceProfile);
_settings.Save();
return true;
}
public bool RenameProfile(string oldName, string newName)
{
WorkspaceProfile workspaceProfile = _settings.Settings.Profiles.FirstOrDefault((WorkspaceProfile p) => p.Name.Equals(oldName, StringComparison.OrdinalIgnoreCase));
if (workspaceProfile == null)
{
return false;
}
workspaceProfile.Name = newName;
_settings.Save();
return true;
}
private static nint FindMatchingWindow(WindowSnapshot snapshot)
{
nint found = IntPtr.Zero;
EnumWindows(delegate(nint hWnd, nint _)
{
string processPath = GetProcessPath(hWnd);
if (string.Equals(processPath, snapshot.Exe, StringComparison.OrdinalIgnoreCase))
{
found = hWnd;
return false;
}
return true;
}, IntPtr.Zero);
return found;
}
private static async Task<nint> WaitForWindowAsync(string exePath, TimeSpan timeout, CancellationToken ct)
{
DateTime deadline = DateTime.UtcNow + timeout;
while (DateTime.UtcNow < deadline)
{
ct.ThrowIfCancellationRequested();
nint hWnd = FindMatchingWindow(new WindowSnapshot
{
Exe = exePath
});
if (hWnd != IntPtr.Zero)
{
return hWnd;
}
await Task.Delay(200, ct);
}
return IntPtr.Zero;
}
private static string GetWindowTitle(nint hWnd)
{
StringBuilder stringBuilder = new StringBuilder(256);
GetWindowText(hWnd, stringBuilder, stringBuilder.Capacity);
return stringBuilder.ToString();
}
private static string GetProcessPath(nint hWnd)
{
try
{
GetWindowThreadProcessId(hWnd, out var lpdwProcessId);
if (lpdwProcessId == 0)
{
return "";
}
Process processById = Process.GetProcessById((int)lpdwProcessId);
return processById.MainModule?.FileName ?? "";
}
catch
{
return "";
}
}
private static bool IsSystemWindow(nint hWnd)
{
StringBuilder stringBuilder = new StringBuilder(256);
GetClassName(hWnd, stringBuilder, stringBuilder.Capacity);
switch (stringBuilder.ToString())
{
case "Shell_TrayWnd":
case "Progman":
case "WorkerW":
case "DV2ControlHost":
return true;
default:
return false;
}
}
private static Dictionary<nint, int> BuildMonitorMap()
{
List<nint> monitors = new List<nint>();
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, delegate(nint hMonitor, nint hdcMonitor, ref RECT lprc, nint dwData)
{
monitors.Add(hMonitor);
return true;
}, IntPtr.Zero);
return monitors.Select((nint hm, int idx) => (hm: hm, idx: idx)).ToDictionary(((nint hm, int idx) t) => t.hm, ((nint hm, int idx) t) => t.idx);
}
private static int GetMonitorIndex(nint hWnd, Dictionary<nint, int> map)
{
nint key = MonitorFromWindow(hWnd, 2u);
int value;
return map.TryGetValue(key, out value) ? value : 0;
}
private static int GetMonitorCount()
{
int count = 0;
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, delegate
{
count++;
return true;
}, IntPtr.Zero);
return count;
}
[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 bool IsIconic(nint hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowText(nint hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(nint hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern bool GetWindowPlacement(nint hWnd, out WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(nint hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern int GetClassName(nint hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(nint hWnd, nint hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
private static extern bool ShowWindow(nint hWnd, int nCmdShow);
[DllImport("user32.dll")]
private static extern nint MonitorFromWindow(nint hwnd, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool EnumDisplayMonitors(nint hdc, nint lprcClip, MonitorEnumProc lpfnEnum, nint dwData);
}