using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Forms; using System.Windows.Threading; using AxCopilot.SDK; using AxCopilot.Services; using AxCopilot.Views; using Microsoft.Win32; namespace AxCopilot.Handlers; public class SystemInfoHandler : IActionHandler { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 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; } internal sealed record InfoAction(string Type, string? Payload = null); private static volatile PerformanceCounter? _cpuCounter; private static float _cpuCached; private static DateTime _cpuUpdated; private static readonly object _cpuLock; public string? Prefix => "info"; public PluginMetadata Metadata => new PluginMetadata("SystemInfo", "시스템 정보 — IP, 배터리, 볼륨, 가동시간 등", "1.0", "AX"); [DllImport("winmm.dll")] private static extern int waveOutGetVolume(nint hwo, out uint dwVolume); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); static SystemInfoHandler() { _cpuUpdated = DateTime.MinValue; _cpuLock = new object(); Task.Run(delegate { try { PerformanceCounter performanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); performanceCounter.NextValue(); _cpuCounter = performanceCounter; } catch { } }); } private static float GetCpuUsage() { try { PerformanceCounter cpuCounter = _cpuCounter; if (cpuCounter == null) { return -1f; } lock (_cpuLock) { if ((DateTime.Now - _cpuUpdated).TotalMilliseconds > 800.0) { _cpuCached = cpuCounter.NextValue(); _cpuUpdated = DateTime.Now; } return _cpuCached; } } catch { return -1f; } } public Task> GetItemsAsync(string query, CancellationToken ct) { string text = query.Trim().ToLowerInvariant(); List list = new List(); try { bool flag = string.IsNullOrEmpty(text); bool flag2 = flag || text.Contains("ip") || text.Contains("네트워크") || text.Contains("network"); bool flag3 = flag || text.Contains("bat") || text.Contains("배터리") || text.Contains("battery"); bool flag4 = flag || text.Contains("vol") || text.Contains("볼륨") || text.Contains("volume"); bool flag5 = flag || text.Contains("up") || text.Contains("가동") || text.Contains("uptime"); bool flag6 = flag || text.Contains("sys") || text.Contains("시스템") || text.Contains("system") || text.Contains("host") || text.Contains("호스트") || text.Contains("os"); bool flag7 = flag || text.Contains("user") || text.Contains("사용자"); bool flag8 = flag || text.Contains("cpu") || text.Contains("프로세서") || text.Contains("processor"); bool flag9 = flag || text.Contains("ram") || text.Contains("메모리") || text.Contains("memory"); bool flag10 = flag || text.Contains("disk") || text.Contains("디스크") || text.Contains("storage") || text.Contains("저장"); bool flag11 = flag || text.Contains("screen") || text.Contains("화면") || text.Contains("resolution") || text.Contains("display"); if (flag6) { list.Add(new LauncherItem("컴퓨터: " + Environment.MachineName, "OS: " + GetOsVersion() + " · Enter로 시스템 정보 열기", null, new InfoAction("shell", "msinfo32"), null, "\ue7f4")); } if (flag7) { string text2 = Environment.UserDomainName + "\\" + Environment.UserName; list.Add(new LauncherItem("사용자: " + Environment.UserName, text2 + " · Enter로 사용자 계정 설정 열기", null, new InfoAction("shell", "netplwiz"), null, "\ue77b")); } if (flag2) { string localIpAddress = GetLocalIpAddress(); if (localIpAddress != null) { list.Add(new LauncherItem("로컬 IP: " + localIpAddress, "LAN / Wi-Fi 주소 · Enter로 네트워크 설정 열기", null, new InfoAction("ms_settings", "ms-settings:network"), null, "\ue968")); } string defaultGateway = GetDefaultGateway(); if (defaultGateway != null) { list.Add(new LauncherItem("게이트웨이: " + defaultGateway, "기본 게이트웨이 · Enter로 네트워크 설정 열기", null, new InfoAction("ms_settings", "ms-settings:network"), null, "\ue968")); } } if (flag3) { LauncherItem batteryItem = GetBatteryItem(); if (batteryItem != null) { list.Add(batteryItem); } } if (flag4) { LauncherItem volumeItem = GetVolumeItem(); if (volumeItem != null) { list.Add(volumeItem); } } if (flag5) { TimeSpan timeSpan = TimeSpan.FromMilliseconds(Environment.TickCount64); string text3 = FormatUptime(timeSpan); DateTime value = DateTime.Now - timeSpan; list.Add(MakeItem("가동 시간: " + text3, $"마지막 재시작: {value:MM/dd HH:mm}", text3, "\ue823")); } if (flag8) { float cpuUsage = GetCpuUsage(); string title = ((cpuUsage < 0f) ? "CPU: 측정 중…" : $"CPU: {(int)cpuUsage}%"); string processorName = GetProcessorName(); list.Add(new LauncherItem(title, (string.IsNullOrEmpty(processorName) ? "전체 CPU 사용률" : processorName) + " · Enter로 리소스 모니터 열기", null, new InfoAction("resource_mon"), null, "\ue950")); } if (flag9) { MEMORYSTATUSEX lpBuffer = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf() }; if (GlobalMemoryStatusEx(ref lpBuffer)) { double value2 = (double)lpBuffer.ullTotalPhys / 1024.0 / 1024.0 / 1024.0; double value3 = (double)(lpBuffer.ullTotalPhys - lpBuffer.ullAvailPhys) / 1024.0 / 1024.0 / 1024.0; uint dwMemoryLoad = lpBuffer.dwMemoryLoad; list.Add(new LauncherItem($"RAM: {value3:F1} / {value2:F1} GB ({dwMemoryLoad}%)", $"사용 중: {value3:F1} GB · 여유: {(double)lpBuffer.ullAvailPhys / 1024.0 / 1024.0 / 1024.0:F1} GB · Enter로 리소스 모니터 열기", null, new InfoAction("resource_mon"), null, "\ue950")); } } if (flag10) { foreach (DriveInfo item in from d in DriveInfo.GetDrives() where d.IsReady && d.DriveType == DriveType.Fixed select d) { double num = (double)item.TotalSize / 1024.0 / 1024.0 / 1024.0; double num2 = (double)item.AvailableFreeSpace / 1024.0 / 1024.0 / 1024.0; double num3 = num - num2; int value4 = (int)(num3 / num * 100.0); string value5 = (string.IsNullOrWhiteSpace(item.VolumeLabel) ? item.Name.TrimEnd('\\') : item.VolumeLabel); list.Add(new LauncherItem($"드라이브 {item.Name.TrimEnd('\\')} ({value5}) — {num3:F0} / {num:F0} GB ({value4}%)", $"여유 공간: {num2:F1} GB · Enter로 탐색기 열기", null, new InfoAction("open_drive", item.RootDirectory.FullName), null, "\ueda2")); } } if (flag11) { double primaryScreenWidth = SystemParameters.PrimaryScreenWidth; double primaryScreenHeight = SystemParameters.PrimaryScreenHeight; list.Add(new LauncherItem($"화면: {(int)primaryScreenWidth} × {(int)primaryScreenHeight}", "기본 모니터 해상도 · Enter로 디스플레이 설정 열기", null, new InfoAction("ms_settings", "ms-settings:display"), null, "\ue7f4")); } } catch (Exception ex) { LogService.Warn("시스템 정보 조회 오류: " + ex.Message); list.Add(new LauncherItem("시스템 정보 조회 실패", ex.Message, null, null, null, "\uea39")); } if (list.Count == 0) { list.Add(new LauncherItem("시스템 정보 없음", "ip · battery · volume · uptime · system 키워드로 검색하세요", null, null, null, "\ue946")); } return Task.FromResult((IEnumerable)list); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { object data = item.Data; InfoAction action = data as InfoAction; if ((object)action == null) { return Task.CompletedTask; } switch (action.Type) { case "copy": if (string.IsNullOrWhiteSpace(action.Payload)) { break; } try { ((DispatcherObject)System.Windows.Application.Current).Dispatcher.Invoke((Action)delegate { System.Windows.Clipboard.SetText(action.Payload); }); LogService.Info("클립보드 복사: " + action.Payload); } catch (Exception ex2) { LogService.Warn("클립보드 복사 실패: " + ex2.Message); } break; case "open_drive": if (!string.IsNullOrWhiteSpace(action.Payload)) { try { Process.Start(new ProcessStartInfo("explorer.exe", action.Payload) { UseShellExecute = true }); } catch (Exception ex4) { LogService.Warn("드라이브 열기 실패: " + ex4.Message); } } break; case "resource_mon": ((DispatcherObject)System.Windows.Application.Current).Dispatcher.Invoke((Action)delegate { ResourceMonitorWindow resourceMonitorWindow = System.Windows.Application.Current.Windows.OfType().FirstOrDefault(); if (resourceMonitorWindow != null) { resourceMonitorWindow.Activate(); } else { new ResourceMonitorWindow().Show(); } }); break; case "shell": if (!string.IsNullOrWhiteSpace(action.Payload)) { try { Process.Start(new ProcessStartInfo(action.Payload) { UseShellExecute = true }); } catch (Exception ex3) { LogService.Warn("셸 명령 실행 실패: " + ex3.Message); } } break; case "ms_settings": if (!string.IsNullOrWhiteSpace(action.Payload)) { try { Process.Start(new ProcessStartInfo(action.Payload) { UseShellExecute = true }); } catch (Exception ex) { LogService.Warn("설정 열기 실패: " + ex.Message); } } break; } return Task.CompletedTask; } private static LauncherItem MakeItem(string title, string subtitle, string? copyValue, string symbol) { return new LauncherItem(title, subtitle, null, (copyValue != null) ? new InfoAction("copy", copyValue) : null, null, symbol); } private static string? GetLocalIpAddress() { try { NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { if (networkInterface.OperationalStatus != OperationalStatus.Up) { continue; } NetworkInterfaceType networkInterfaceType = networkInterface.NetworkInterfaceType; if ((networkInterfaceType == NetworkInterfaceType.Loopback || networkInterfaceType == NetworkInterfaceType.Tunnel) ? true : false) { continue; } foreach (UnicastIPAddressInformation unicastAddress in networkInterface.GetIPProperties().UnicastAddresses) { if (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork) { return unicastAddress.Address.ToString(); } } } } catch { } return null; } private static string? GetDefaultGateway() { try { NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface networkInterface in allNetworkInterfaces) { if (networkInterface.OperationalStatus == OperationalStatus.Up) { GatewayIPAddressInformation gatewayIPAddressInformation = networkInterface.GetIPProperties().GatewayAddresses.FirstOrDefault((GatewayIPAddressInformation g) => g.Address.AddressFamily == AddressFamily.InterNetwork); if (gatewayIPAddressInformation != null) { return gatewayIPAddressInformation.Address.ToString(); } } } } catch { } return null; } private static LauncherItem? GetBatteryItem() { try { PowerStatus powerStatus = SystemInformation.PowerStatus; float batteryLifePercent = powerStatus.BatteryLifePercent; if (batteryLifePercent < 0f) { return new LauncherItem("배터리: 해당 없음", "데스크톱 PC 또는 항상 연결됨 · Enter로 전원 설정 열기", null, new InfoAction("ms_settings", "ms-settings:powersleep"), null, "\ue83f"); } int num = (int)(batteryLifePercent * 100f); bool flag = powerStatus.PowerLineStatus == System.Windows.Forms.PowerLineStatus.Online; string text = ((powerStatus.BatteryLifeRemaining >= 0) ? (" · 잔여: " + FormatUptime(TimeSpan.FromSeconds(powerStatus.BatteryLifeRemaining))) : ""); string symbol = (flag ? "\ue83e" : ((num > 50) ? "\ue83f" : "\ueba0")); return new LauncherItem($"배터리: {num}%{(flag ? " ⚡ 충전 중" : "")}", "전원: " + (flag ? "AC 연결됨" : "배터리 사용 중") + text + " · Enter로 전원 설정 열기", null, new InfoAction("ms_settings", "ms-settings:powersleep"), null, symbol); } catch { return null; } } private static LauncherItem? GetVolumeItem() { try { if (waveOutGetVolume(IntPtr.Zero, out var dwVolume) == 0) { int num = (int)((double)(dwVolume & 0xFFFF) / 655.35); int num2 = (int)((double)((dwVolume >> 16) & 0xFFFF) / 655.35); int num3 = (num + num2) / 2; return new LauncherItem($"볼륨: {num3}%", $"L: {num}% · R: {num2}% · Enter로 사운드 설정 열기", null, new InfoAction("ms_settings", "ms-settings:sound"), null, (num3 == 0) ? "\ue74f" : "\ue995"); } } catch { } return null; } private static string GetOsVersion() { try { using RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); if (registryKey != null) { string text = (registryKey.GetValue("ProductName") as string) ?? "Windows"; string text2 = (registryKey.GetValue("CurrentBuildNumber") as string) ?? ""; object value = registryKey.GetValue("UBR"); return (value != null) ? $"{text} (빌드 {text2}.{value})" : (text + " (빌드 " + text2 + ")"); } } catch { } return Environment.OSVersion.ToString(); } private static string GetProcessorName() { try { using RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"); return (registryKey?.GetValue("ProcessorNameString") as string) ?? ""; } catch { return ""; } } private static string FormatUptime(TimeSpan t) { if (!(t.TotalDays >= 1.0)) { if (!(t.TotalHours >= 1.0)) { return $"{t.Minutes}분 {t.Seconds}초"; } return $"{t.Hours}시간 {t.Minutes}분"; } return $"{(int)t.TotalDays}일 {t.Hours}시간 {t.Minutes}분"; } }