Initial commit to new repository
This commit is contained in:
384
.decompiledproj/AxCopilot/Core/ContextManager.cs
Normal file
384
.decompiledproj/AxCopilot/Core/ContextManager.cs
Normal file
@@ -0,0 +1,384 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user