using System; using System.Linq; using System.Threading.Tasks; using TrustedUninstaller.Shared.Exceptions; using TrustedUninstaller.Shared.Tasks; using YamlDotNet.Serialization; using System.Diagnostics; using System.IO; using System.Text; using System.Threading; namespace TrustedUninstaller.Shared.Actions { // Integrate ame-assassin later internal class SystemPackageAction : TaskAction, ITaskAction { public void RunTaskOnMainThread() { throw new NotImplementedException(); } public enum Architecture { amd64 = 0, wow64 = 1, msil = 2, x86 = 3, All = 4 } [YamlMember(typeof(string), Alias = "name")] public string Name { get; set; } [YamlMember(typeof(string), Alias = "arch")] public Architecture Arch { get; set; } = Architecture.All; [YamlMember(typeof(string), Alias = "language")] public string Language { get; set; } = "*"; [YamlMember(typeof(string), Alias = "regexExcludeFiles")] public string[]? RegexExcludeList { get; set; } [YamlMember(typeof(string), Alias = "excludeDependents")] public string[]? ExcludeDependentsList { get; set; } [YamlMember(typeof(string[]), Alias = "weight")] public int ProgressWeight { get; set; } = 15; public int GetProgressWeight() => ProgressWeight; private bool InProgress { get; set; } public void ResetProgress() => InProgress = false; public string ErrorString() => $"SystemPackageAction failed to remove '{Name}'."; public UninstallTaskStatus GetStatus() { if (InProgress) return UninstallTaskStatus.InProgress; return HasFinished ? UninstallTaskStatus.Completed : UninstallTaskStatus.ToDo; } private bool HasFinished = false; public async Task RunTask() { if (InProgress) throw new TaskInProgressException("Another Appx action was called while one was in progress."); InProgress = true; Console.WriteLine($"Removing system package '{Name}'..."); var excludeArgs = new StringBuilder(""); if (RegexExcludeList != null) { foreach (var regex in RegexExcludeList) { excludeArgs.Append(@$" -xf ""{regex}"""); } } var excludeDependsArgs = new StringBuilder(""); if (ExcludeDependentsList != null) { foreach (var dependent in ExcludeDependentsList) { excludeDependsArgs.Append(@$" -xdependent ""{dependent}"""); } } string kernelDriverArg = AmeliorationUtil.UseKernelDriver ? " -UseKernelDriver" : ""; var psi = new ProcessStartInfo() { UseShellExecute = false, CreateNoWindow = true, Arguments = $@"-SystemPackage ""{Name}"" -Arch {Arch.ToString()} -Language ""{Language}""" + excludeArgs + excludeDependsArgs + kernelDriverArg, FileName = Directory.GetCurrentDirectory() + "\\ame-assassin\\ame-assassin.exe", RedirectStandardOutput = true, RedirectStandardError = true }; var proc = Process.Start(psi); proc.OutputDataReceived += ProcOutputHandler; proc.ErrorDataReceived += ProcOutputHandler; proc.BeginOutputReadLine(); proc.BeginErrorReadLine(); bool exited = proc.WaitForExit(30000); // WaitForExit alone seems to not be entirely reliable while (!exited && ExeRunning(proc)) { exited = proc.WaitForExit(30000); } using (var log = new StreamWriter("Logs\\Packages.txt", true)) log.Write(output.ToString()); HasFinished = true; InProgress = false; return true; } private StringBuilder output = new StringBuilder(""); private bool PleaseWait = false; private void ProcOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { var write = outLine == null ? "" : outLine.Data; output.Append(write + Environment.NewLine); if (String.IsNullOrEmpty(write)) return; if (write.StartsWith("--- Removing")) { Console.WriteLine(write.Substring(4, write.Length - 4)); PleaseWait = true; } if (write.StartsWith("Waiting for the service to stop...") && PleaseWait) { PleaseWait = false; Console.WriteLine("This may take some time..."); } } private static bool ExeRunning(Process process) { try { return Process.GetProcessesByName(process.ProcessName).Any(x => x.Id == process.Id); } catch (Exception) { return false; } } } }