using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using AxCopilot.SDK; using AxCopilot.Services; using Microsoft.Win32; namespace AxCopilot.Handlers; public class UninstallHandler : IActionHandler { private static readonly string[] _regPaths = new string[2] { "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall", "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall" }; private static (DateTime At, List Apps)? _cache; private static readonly TimeSpan CacheTtl = TimeSpan.FromSeconds(30.0); public string? Prefix => "uninstall"; public PluginMetadata Metadata => new PluginMetadata("Uninstall", "앱 제거 — uninstall 뒤에 앱 이름 입력", "1.0", "AX"); public Task> GetItemsAsync(string query, CancellationToken ct) { string q = query.Trim(); if (string.IsNullOrWhiteSpace(q)) { return Task.FromResult((IEnumerable)new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("제거할 앱 이름을 입력하세요", "예: uninstall chrome · uninstall office", null, null, null, "\ue74d"))); } List installedApps = GetInstalledApps(); List source = (from a in installedApps where a.Name.Contains(q, StringComparison.OrdinalIgnoreCase) || (a.Publisher?.Contains(q, StringComparison.OrdinalIgnoreCase) ?? false) orderby a.Name select a).Take(12).ToList(); if (!source.Any()) { return Task.FromResult((IEnumerable)new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("검색 결과 없음", "'" + q + "'에 해당하는 설치된 앱이 없습니다", null, null, null, "\ue946"))); } List result = source.Select((InstalledApp a) => new LauncherItem(a.Name, BuildSubtitle(a), null, a, null, "\ue74d")).ToList(); return Task.FromResult((IEnumerable)result); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (!(item.Data is InstalledApp installedApp)) { return Task.CompletedTask; } if (string.IsNullOrWhiteSpace(installedApp.UninstallString)) { LogService.Warn("제거 문자열 없음: " + installedApp.Name); return Task.CompletedTask; } try { string text = installedApp.UninstallString.Trim(); if (text.StartsWith("msiexec", StringComparison.OrdinalIgnoreCase)) { string arguments = text.Substring("msiexec".Length).Trim(); Process.Start(new ProcessStartInfo("msiexec.exe", arguments) { UseShellExecute = true }); } else { string fileName; string arguments2; if (text.StartsWith('"')) { int num = text.IndexOf('"', 1); fileName = ((num > 0) ? text.Substring(1, num - 1) : text); object obj; if (num <= 0) { obj = ""; } else { string text2 = text; int num2 = num + 1; obj = text2.Substring(num2, text2.Length - num2).Trim(); } arguments2 = (string)obj; } else { int num3 = text.IndexOf(' '); if (num3 > 0) { fileName = text.Substring(0, num3); string text2 = text; int num2 = num3 + 1; arguments2 = text2.Substring(num2, text2.Length - num2); } else { fileName = text; arguments2 = ""; } } ProcessStartInfo startInfo = new ProcessStartInfo(fileName, arguments2) { UseShellExecute = true }; Process.Start(startInfo); } } catch (Exception ex) { LogService.Warn("제거 실행 실패 [" + installedApp.Name + "]: " + ex.Message); } return Task.CompletedTask; } private static string BuildSubtitle(InstalledApp a) { List list = new List(); if (!string.IsNullOrWhiteSpace(a.Publisher)) { list.Add(a.Publisher); } if (!string.IsNullOrWhiteSpace(a.Version)) { list.Add("v" + a.Version); } if (!string.IsNullOrWhiteSpace(a.InstallDate)) { list.Add(a.InstallDate); } list.Add("Enter로 제거"); return string.Join(" · ", list); } private static List GetInstalledApps() { if (_cache.HasValue && DateTime.Now - _cache.Value.At < CacheTtl) { return _cache.Value.Apps; } List list = new List(); HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); RegistryKey[] array = new RegistryKey[2] { Registry.LocalMachine, Registry.CurrentUser }; foreach (RegistryKey registryKey in array) { string[] regPaths = _regPaths; foreach (string name in regPaths) { try { using RegistryKey registryKey2 = registryKey.OpenSubKey(name); if (registryKey2 == null) { continue; } string[] subKeyNames = registryKey2.GetSubKeyNames(); foreach (string name2 in subKeyNames) { try { using RegistryKey registryKey3 = registryKey2.OpenSubKey(name2); if (registryKey3 != null) { string text = registryKey3.GetValue("DisplayName") as string; string text2 = registryKey3.GetValue("UninstallString") as string; if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2) && (!text.StartsWith("KB", StringComparison.OrdinalIgnoreCase) || text.Length >= 12) && hashSet.Add(text) && (!(registryKey3.GetValue("SystemComponent") is int num) || num != 1)) { list.Add(new InstalledApp { Name = text, UninstallString = text2, Publisher = (registryKey3.GetValue("Publisher") as string), Version = (registryKey3.GetValue("DisplayVersion") as string), InstallDate = FormatDate(registryKey3.GetValue("InstallDate") as string) }); } } } catch { } } } catch { } } } list.Sort((InstalledApp a, InstalledApp b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); _cache = (DateTime.Now, list); return list; } private static string? FormatDate(string? raw) { if (raw != null && raw.Length == 8 && int.TryParse(raw, out var _)) { return $"{raw.Substring(0, 4)}-{raw.Substring(4, 2)}-{raw.Substring(6, 2)}"; } return null; } }