Skip to content

Commit ea7eb55

Browse files
committed
feat: compute true disk footprint per program by walking InstallLocation + AppData paths
1 parent e9cc1d8 commit ea7eb55

2 files changed

Lines changed: 66 additions & 0 deletions

File tree

src/DeepPurge.Core/Models/InstalledProgram.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class InstalledProgram : INotifyPropertyChanged
2020
public string QuietUninstallString { get; set; } = string.Empty;
2121
public string DisplayIconPath { get; set; } = string.Empty;
2222
public long EstimatedSizeKB { get; set; }
23+
public long ActualSizeBytes { get; set; } = -1;
2324
public bool IsSystemComponent { get; set; }
2425
public bool IsWindowsInstaller { get; set; }
2526
public string ParentKeyName { get; set; } = string.Empty;
@@ -49,6 +50,7 @@ public string EstimatedSizeDisplay
4950
{
5051
get
5152
{
53+
if (ActualSizeBytes > 0) return FormatBytes(ActualSizeBytes);
5254
if (EstimatedSizeKB <= 0) return "";
5355
if (EstimatedSizeKB < 1024) return $"{EstimatedSizeKB} KB";
5456
double mb = EstimatedSizeKB / 1024.0;
@@ -57,6 +59,15 @@ public string EstimatedSizeDisplay
5759
}
5860
}
5961

62+
private static string FormatBytes(long bytes)
63+
{
64+
if (bytes < 1024) return $"{bytes} B";
65+
double kb = bytes / 1024.0;
66+
if (kb < 1024) return $"{kb:F0} KB";
67+
double mb = kb / 1024.0;
68+
return mb < 1024 ? $"{mb:F1} MB" : $"{mb / 1024.0:F2} GB";
69+
}
70+
6071
public string InstallDateDisplay
6172
{
6273
get

src/DeepPurge.Core/Registry/InstalledProgramScanner.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,59 @@ public static string GetSilentUninstallCommand(InstalledProgram program)
170170
_ => ""
171171
};
172172
}
173+
174+
public static void ComputeActualSizes(IList<InstalledProgram> programs)
175+
{
176+
Parallel.ForEach(programs, new ParallelOptions { MaxDegreeOfParallelism = 4 }, prog =>
177+
{
178+
long total = 0;
179+
var paths = new List<string>();
180+
181+
if (!string.IsNullOrEmpty(prog.InstallLocation) && Directory.Exists(prog.InstallLocation))
182+
paths.Add(prog.InstallLocation);
183+
184+
var appDataDirs = new[]
185+
{
186+
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
187+
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
188+
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
189+
};
190+
191+
var nameTerms = new List<string>();
192+
if (!string.IsNullOrEmpty(prog.DisplayName)) nameTerms.Add(prog.DisplayName);
193+
if (!string.IsNullOrEmpty(prog.InstallLocation))
194+
{
195+
var folderName = Path.GetFileName(prog.InstallLocation.TrimEnd('\\'));
196+
if (!string.IsNullOrEmpty(folderName) && folderName.Length > 2) nameTerms.Add(folderName);
197+
}
198+
199+
foreach (var appDataDir in appDataDirs)
200+
{
201+
if (string.IsNullOrEmpty(appDataDir) || !Directory.Exists(appDataDir)) continue;
202+
try
203+
{
204+
foreach (var sub in Directory.GetDirectories(appDataDir))
205+
{
206+
var subName = Path.GetFileName(sub);
207+
if (nameTerms.Any(t => subName.Equals(t, StringComparison.OrdinalIgnoreCase)))
208+
paths.Add(sub);
209+
}
210+
}
211+
catch { }
212+
}
213+
214+
foreach (var path in paths.Distinct(StringComparer.OrdinalIgnoreCase))
215+
{
216+
try
217+
{
218+
total += new DirectoryInfo(path)
219+
.EnumerateFiles("*", SearchOption.AllDirectories)
220+
.Sum(fi => { try { return fi.Length; } catch { return 0L; } });
221+
}
222+
catch { }
223+
}
224+
225+
if (total > 0) prog.ActualSizeBytes = total;
226+
});
227+
}
173228
}

0 commit comments

Comments
 (0)