209 lines
6.1 KiB
C#
209 lines
6.1 KiB
C#
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<InstalledApp> 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<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
|
|
{
|
|
string q = query.Trim();
|
|
if (string.IsNullOrWhiteSpace(q))
|
|
{
|
|
return Task.FromResult((IEnumerable<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("제거할 앱 이름을 입력하세요", "예: uninstall chrome · uninstall office", null, null, null, "\ue74d")));
|
|
}
|
|
List<InstalledApp> installedApps = GetInstalledApps();
|
|
List<InstalledApp> 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<LauncherItem>)new _003C_003Ez__ReadOnlySingleElementList<LauncherItem>(new LauncherItem("검색 결과 없음", "'" + q + "'에 해당하는 설치된 앱이 없습니다", null, null, null, "\ue946")));
|
|
}
|
|
List<LauncherItem> result = source.Select((InstalledApp a) => new LauncherItem(a.Name, BuildSubtitle(a), null, a, null, "\ue74d")).ToList();
|
|
return Task.FromResult((IEnumerable<LauncherItem>)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<string> list = new List<string>();
|
|
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<InstalledApp> GetInstalledApps()
|
|
{
|
|
if (_cache.HasValue && DateTime.Now - _cache.Value.At < CacheTtl)
|
|
{
|
|
return _cache.Value.Apps;
|
|
}
|
|
List<InstalledApp> list = new List<InstalledApp>();
|
|
HashSet<string> hashSet = new HashSet<string>(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;
|
|
}
|
|
}
|