using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows; using AxCopilot.SDK; using AxCopilot.Themes; namespace AxCopilot.Handlers; /// /// 시스템 리소스 모니터 핸들러. "monitor" 프리픽스로 사용합니다. /// CPU, 메모리, 디스크, 프로세스 수 등 실시간 시스템 리소스 정보를 표시합니다. /// 예: monitor → 전체 리소스 현황 /// monitor cpu → CPU 관련 정보만 /// monitor mem → 메모리 관련 정보만 /// monitor disk → 디스크 사용량 /// Enter → 결과를 클립보드에 복사. /// public class MonitorHandler : IActionHandler { public string? Prefix => "monitor"; public PluginMetadata Metadata => new( "Monitor", "시스템 리소스 모니터 — monitor", "1.0", "AX"); [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); [StructLayout(LayoutKind.Sequential)] private struct MEMORYSTATUSEX { public uint dwLength; public uint dwMemoryLoad; public ulong ullTotalPhys; public ulong ullAvailPhys; public ulong ullTotalPageFile; public ulong ullAvailPageFile; public ulong ullTotalVirtual; public ulong ullAvailVirtual; public ulong ullAvailExtendedVirtual; } public Task> GetItemsAsync(string query, CancellationToken ct) { var q = query.Trim().ToLowerInvariant(); var items = new List(); var showAll = string.IsNullOrWhiteSpace(q); // CPU if (showAll || q.Contains("cpu") || q.Contains("프로세서")) { var cpuCount = Environment.ProcessorCount; var processes = Process.GetProcesses().Length; var threads = 0; try { threads = Process.GetProcesses().Sum(p => { try { return p.Threads.Count; } catch { return 0; } }); } catch { } items.Add(new LauncherItem( $"CPU: {cpuCount}코어 · 프로세스 {processes}개 · 스레드 {threads:N0}개", "Enter로 클립보드 복사", null, $"CPU: {cpuCount}코어, 프로세스 {processes}개, 스레드 {threads:N0}개", Symbol: Symbols.Processor)); } // Memory if (showAll || q.Contains("mem") || q.Contains("ram") || q.Contains("메모리")) { var mem = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf() }; GlobalMemoryStatusEx(ref mem); var totalGB = mem.ullTotalPhys / (1024.0 * 1024 * 1024); var usedGB = (mem.ullTotalPhys - mem.ullAvailPhys) / (1024.0 * 1024 * 1024); var pct = mem.dwMemoryLoad; items.Add(new LauncherItem( $"메모리: {usedGB:F1}GB / {totalGB:F1}GB ({pct}% 사용)", $"사용 가능: {mem.ullAvailPhys / (1024.0 * 1024 * 1024):F1}GB · Enter로 복사", null, $"메모리: {usedGB:F1}GB / {totalGB:F1}GB ({pct}% 사용)", Symbol: Symbols.Memory)); } // Disk if (showAll || q.Contains("disk") || q.Contains("디스크") || q.Contains("저장")) { foreach (var drive in System.IO.DriveInfo.GetDrives()) { if (!drive.IsReady || drive.DriveType != System.IO.DriveType.Fixed) continue; var totalGB = drive.TotalSize / (1024.0 * 1024 * 1024); var freeGB = drive.AvailableFreeSpace / (1024.0 * 1024 * 1024); var usedGB = totalGB - freeGB; var pct = (int)(usedGB / totalGB * 100); items.Add(new LauncherItem( $"디스크 {drive.Name.TrimEnd('\\')} {usedGB:F0}GB / {totalGB:F0}GB ({pct}%)", $"여유: {freeGB:F1}GB · {drive.DriveFormat} · Enter로 복사", null, $"디스크 {drive.Name}: {usedGB:F0}GB / {totalGB:F0}GB ({pct}%), 여유 {freeGB:F1}GB", Symbol: Symbols.Storage)); } } // Uptime if (showAll || q.Contains("uptime") || q.Contains("가동")) { var uptime = TimeSpan.FromMilliseconds(Environment.TickCount64); var uptimeStr = uptime.Days > 0 ? $"{uptime.Days}일 {uptime.Hours}시간 {uptime.Minutes}분" : $"{uptime.Hours}시간 {uptime.Minutes}분"; items.Add(new LauncherItem( $"가동 시간: {uptimeStr}", "마지막 재시작 이후 경과 · Enter로 복사", null, $"가동 시간: {uptimeStr}", Symbol: Symbols.Clock)); } // Top processes by memory if (showAll || q.Contains("top") || q.Contains("프로세스")) { try { var topProcs = Process.GetProcesses() .Where(p => { try { return p.WorkingSet64 > 0; } catch { return false; } }) .OrderByDescending(p => { try { return p.WorkingSet64; } catch { return 0L; } }) .Take(5) .Select(p => { try { return $"{p.ProcessName} ({p.WorkingSet64 / (1024 * 1024)}MB)"; } catch { return p.ProcessName; } }); items.Add(new LauncherItem( "메모리 상위 프로세스", string.Join(", ", topProcs), null, $"메모리 상위: {string.Join(", ", topProcs)}", Symbol: Symbols.Computer)); } catch { } } if (items.Count == 0) { items.Add(new LauncherItem( $"'{q}'에 해당하는 리소스 항목 없음", "cpu / mem / disk / uptime / top", null, null, Symbol: Symbols.Warning)); } return Task.FromResult>(items); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.Data is string text && !string.IsNullOrWhiteSpace(text)) { try { Application.Current?.Dispatcher.Invoke(() => Clipboard.SetText(text)); } catch { } } return Task.CompletedTask; } }