-
Notifications
You must be signed in to change notification settings - Fork 30
Expand file tree
/
Copy pathCleanerEngine.cs
More file actions
67 lines (64 loc) · 5.48 KB
/
Copy pathCleanerEngine.cs
File metadata and controls
67 lines (64 loc) · 5.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using System.Text.Json;
using System.Runtime.InteropServices;
namespace EasySystemCleaner;
public sealed record CleanRule(string Id, string Category, string Name, string Description, string Path, string Pattern = "*", bool Recursive = true);
public sealed record ScanItem(CleanRule Rule, int Files, long Bytes, IReadOnlyList<string> Examples);
public sealed record CleanResult(int DeletedFiles, long FreedBytes, IReadOnlyList<string> Errors);
public sealed class CleanerEngine
{
private readonly string _basePath;
private string SettingsPath => Path.Combine(_basePath, "EasySystemCleaner.settings.json");
private string LogPath => Path.Combine(_basePath, "EasySystemCleaner.log");
public CleanerEngine(string basePath) => _basePath = basePath;
public IReadOnlyList<CleanRule> Rules { get; } = new List<CleanRule>
{
new("windows-temp", "Windows", "Temporary files", "Files created temporarily by Windows and applications.", Path.GetTempPath()),
new("windows-crash", "Windows", "Crash reports", "Local crash report archives.", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "CrashDumps")),
new("windows-wer", "Windows", "Error reports", "Windows Error Reporting archives.", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "Windows", "WER", "ReportArchive")),
new("windows-thumb", "Windows", "Thumbnail cache", "Explorer image and video thumbnail cache.", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "Windows", "Explorer"), "thumbcache*.db", false),
new("edge-cache", "Browsers", "Microsoft Edge cache", "Cached web content. You may be signed out of some websites.", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "Edge", "User Data", "Default", "Cache")),
new("chrome-cache", "Browsers", "Google Chrome cache", "Cached web content. You may be signed out of some websites.", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Google", "Chrome", "User Data", "Default", "Cache")),
new("firefox-cache", "Browsers", "Mozilla Firefox cache", "Cached web content.", Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Mozilla", "Firefox", "Profiles"), "*", true),
new("recycle-bin", "Windows", "Recycle Bin", "Permanently removes items currently in the Recycle Bin.", "::RECYCLEBIN::")
};
public IReadOnlySet<string> LoadSelection() { try { return JsonSerializer.Deserialize<string[]>(File.ReadAllText(SettingsPath))?.ToHashSet() ?? Rules.Select(r => r.Id).ToHashSet(); } catch { return Rules.Select(r => r.Id).ToHashSet(); } }
public void SaveSelection(IEnumerable<string> ids) => File.WriteAllText(SettingsPath, JsonSerializer.Serialize(ids.Order()));
public IReadOnlyList<ScanItem> Scan(IEnumerable<string> selected) { var chosen = selected.ToHashSet(); return Rules.Where(r => chosen.Contains(r.Id)).Select(ScanRule).ToList(); }
private static ScanItem ScanRule(CleanRule rule)
{
if (rule.Path == "::RECYCLEBIN::") return new(rule, 0, 0, new[] { "Size is calculated by Windows when cleaning." });
if (!Directory.Exists(rule.Path)) return new(rule, 0, 0, Array.Empty<string>());
try { var files = FindFiles(rule).Select(path => new FileInfo(path)).ToList(); return new(rule, files.Count, files.Sum(f => SafeLength(f)), files.Take(3).Select(f => f.FullName).ToList()); }
catch { return new(rule, 0, 0, new[] { "This location cannot be read (it may be in use)." }); }
}
public CleanResult Clean(IEnumerable<string> selected)
{
var errors = new List<string>(); var files = 0; long bytes = 0;
foreach (var rule in Rules.Where(r => selected.Contains(r.Id)))
{
if (rule.Path == "::RECYCLEBIN::")
{
try
{
var result = SHEmptyRecycleBin(IntPtr.Zero, null, 0x00000001 | 0x00000002 | 0x00000004);
if (result != 0) errors.Add($"{rule.Name}: Windows returned error 0x{result:X8}.");
}
catch (Exception e) { errors.Add($"{rule.Name}: {e.Message}"); }
continue;
}
if (!Directory.Exists(rule.Path)) continue;
try { foreach (var path in FindFiles(rule)) { try { var size = new FileInfo(path).Length; File.Delete(path); files++; bytes += size; } catch (Exception e) { errors.Add($"{Path.GetFileName(path)}: {e.Message}"); } } }
catch (Exception e) { errors.Add($"{rule.Name}: {e.Message}"); }
}
return new(files, bytes, errors);
}
public void AppendLog(string text) => File.AppendAllText(LogPath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {text}{Environment.NewLine}");
private static long SafeLength(FileInfo file) { try { return file.Length; } catch { return 0; } }
private static IEnumerable<string> FindFiles(CleanRule rule)
{
if (rule.Id == "firefox-cache") return Directory.EnumerateDirectories(rule.Path, "cache2", SearchOption.AllDirectories).SelectMany(folder => Directory.EnumerateFiles(folder, "*", SearchOption.AllDirectories));
return Directory.EnumerateFiles(rule.Path, rule.Pattern, rule.Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
}
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
private static extern int SHEmptyRecycleBin(IntPtr hwnd, string? rootPath, uint flags);
}