463 lines
15 KiB
C#
463 lines
15 KiB
C#
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<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
||
{
|
||
string text = query.Trim().ToLowerInvariant();
|
||
List<LauncherItem> list = new List<LauncherItem>();
|
||
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<MEMORYSTATUSEX>()
|
||
};
|
||
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<LauncherItem>)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<ResourceMonitorWindow>().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}분";
|
||
}
|
||
}
|