gistfile1.txt
· 25 KiB · Text
Исходник
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Win32;
namespace DiskUsageLog
{
class Program
{
const long ONE_GB = 1024L * 1024 * 1024;
const long ONE_MB = 1024L * 1024;
static readonly string[] HardExcludedFolders =
{
"WinSxS",
"Microsoft.NET",
"assembly"
};
static readonly string InstallerPath = @"C:\Windows\Installer";
static readonly string[] KnownCacheFolderNames =
{
"Temp", "tmp", "Logs", "LogFiles",
"INetCache", "Temporary Internet Files",
"WER", "CrashDumps"
};
static Dictionary<string, ExtensionStat> ExtensionStats =
new Dictionary<string, ExtensionStat>(StringComparer.OrdinalIgnoreCase);
static List<FileInfo> LargestFiles = new List<FileInfo>();
static List<LargeFolder> LargeFolders = new List<LargeFolder>();
static List<FileInfo> StaleFiles = new List<FileInfo>();
static List<LargeFolder> RootBreakdown = new List<LargeFolder>();
static List<CacheFolder> DetectedCacheFolders = new List<CacheFolder>();
static List<LargeFolder> SystemDiagResults = new List<LargeFolder>();
static long TotalFilesScanned;
static long TotalFoldersScanned;
static long TotalErrorCount;
static long TotalSizeScanned;
static Stopwatch Timer = new Stopwatch();
static int spinnerIndex;
static readonly char[] SpinnerChars = { '|', '/', '-', '\\' };
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
string rootPath = args.Length > 0 ? args[0] : @"C:\";
if (!Directory.Exists(rootPath))
{
Console.WriteLine("Chemin invalide.");
return;
}
Console.WriteLine("DiskUsageLog - Analyse disque");
Console.WriteLine("Racine analysée : " + rootPath);
DisplayXstoreInfo();
Console.WriteLine("--------------------------------------------------\n");
Timer.Start();
string[] rootDirs;
try { rootDirs = Directory.GetDirectories(rootPath); }
catch { rootDirs = new string[0]; }
foreach (string dir in rootDirs)
{
long size = AnalyzeDirectory(dir);
RootBreakdown.Add(new LargeFolder
{
Path = dir,
Size = size
});
}
ClearProgress();
Timer.Stop();
DisplaySummary();
DisplayRootBreakdown();
DisplayLargeFolders();
DisplayTopExtensions();
DisplayLargestFiles();
DisplayStaleFiles();
DisplayCacheFolders();
DisplaySystemDiagnostic();
DisplayOrphanInstallers();
ExportReport(rootPath);
Console.WriteLine("\nAnalyse terminée.");
}
// ======================= ANALYSE =======================
static long AnalyzeDirectory(string path)
{
long totalSize = 0;
TotalFoldersScanned++;
string folderName = Path.GetFileName(path);
bool isKnownCache = KnownCacheFolderNames.Any(c =>
c.Equals(folderName, StringComparison.OrdinalIgnoreCase));
try
{
foreach (string file in Directory.GetFiles(path))
{
FileInfo fi = new FileInfo(file);
totalSize += fi.Length;
TotalFilesScanned++;
TotalSizeScanned += fi.Length;
bool excluded = IsExcludedFile(fi, path);
if (!IsSystemCritical(path) && !excluded)
TrackExtension(fi);
if (!excluded)
{
TrackLargestFiles(fi);
TrackStaleFiles(fi);
}
}
if (TotalFoldersScanned % 100 == 0)
UpdateProgress(path);
foreach (string dir in Directory.GetDirectories(path))
{
if (IsHardExcluded(dir)) continue;
totalSize += AnalyzeDirectory(dir);
}
}
catch
{
TotalErrorCount++;
}
if (isKnownCache && totalSize >= 50 * ONE_MB)
{
DetectedCacheFolders.Add(new CacheFolder
{
Path = path,
Size = totalSize,
FolderName = folderName
});
}
if (totalSize >= ONE_GB)
{
LargeFolders.Add(new LargeFolder
{
Path = path,
Size = totalSize,
IsRiskArea = IsRiskArea(path)
});
}
return totalSize;
}
// ======================= REGLES =======================
static bool IsHardExcluded(string path)
{
string name = Path.GetFileName(path);
return HardExcludedFolders.Any(e =>
e.Equals(name, StringComparison.OrdinalIgnoreCase));
}
static bool IsSystemCritical(string path)
{
return path.StartsWith(@"C:\Windows\System32",
StringComparison.OrdinalIgnoreCase);
}
static bool IsRiskArea(string path)
{
return path.StartsWith(@"C:\Windows\Installer", StringComparison.OrdinalIgnoreCase)
|| path.StartsWith(@"C:\Windows\Temp", StringComparison.OrdinalIgnoreCase)
|| path.StartsWith(@"C:\Windows\SoftwareDistribution", StringComparison.OrdinalIgnoreCase)
|| path.Contains(@"\AppData\Local\Temp");
}
// ======================= EXCLUSIONS =======================
static bool IsExcludedFile(FileInfo fi, string dirPath)
{
if (fi.Extension.Equals(".dbf", StringComparison.OrdinalIgnoreCase)
&& dirPath.StartsWith(@"C:\xstoredb", StringComparison.OrdinalIgnoreCase))
return true;
return false;
}
// ======================= EXTENSIONS =======================
static void TrackExtension(FileInfo fi)
{
string ext = string.IsNullOrEmpty(fi.Extension)
? "[sans_ext]"
: fi.Extension.ToLower();
if (!ExtensionStats.ContainsKey(ext))
ExtensionStats[ext] = new ExtensionStat();
ExtensionStats[ext].Count++;
ExtensionStats[ext].Size += fi.Length;
}
static void DisplayTopExtensions()
{
Console.WriteLine("\n=== TOP 20 EXTENSIONS (hors système critique) ===\n");
Console.WriteLine("{0,-12} {1,10} {2,15}",
"Extension", "Fichiers", "Taille (GB)");
foreach (var ext in ExtensionStats
.OrderByDescending(e => e.Value.Size)
.Take(20))
{
Console.WriteLine("{0,-12} {1,10} {2,15}",
ext.Key,
ext.Value.Count,
ToGB(ext.Value.Size));
}
}
// ======================= GROS FICHIERS =======================
static void TrackLargestFiles(FileInfo fi)
{
LargestFiles.Add(fi);
LargestFiles = LargestFiles
.OrderByDescending(f => f.Length)
.Take(10)
.ToList();
}
static void DisplayLargestFiles()
{
Console.WriteLine("\n=== TOP 10 PLUS GROS FICHIERS ===\n");
foreach (FileInfo fi in LargestFiles)
{
Console.WriteLine("{0} : {1} GB",
fi.FullName,
ToGB(fi.Length));
}
}
// ======================= DOSSIERS =======================
static void DisplayLargeFolders()
{
Console.WriteLine("\n=== DOSSIERS > 1 Go ===\n");
foreach (LargeFolder f in LargeFolders
.OrderByDescending(f => f.Size))
{
string flag = f.IsRiskArea ? " !!!" : "";
Console.WriteLine("{0} : {1} GB{2}",
f.Path,
ToGB(f.Size),
flag);
}
Console.WriteLine("\n!!! = zone connue à dérive disque");
}
// ======================= MSI ORPHELINS =======================
static void DisplayOrphanInstallers()
{
Console.WriteLine("\n=== MSI / MSP ORPHELINS POTENTIELS ===\n");
if (!Directory.Exists(InstallerPath))
{
Console.WriteLine("Répertoire Windows Installer introuvable.");
return;
}
var allInstallers = GetInstallerFiles();
var referencedInstallers = GetReferencedInstallerFiles();
var orphans = allInstallers.Values
.Where(f => !referencedInstallers.Contains(f.FullName))
.OrderByDescending(f => f.Length)
.ToList();
long total = 0;
foreach (var fi in orphans)
{
Console.WriteLine("{0,-20} {1,10} GB",
fi.Name,
ToGB(fi.Length));
total += fi.Length;
}
Console.WriteLine("\nTotal récupérable potentiel : {0} GB",
ToGB(total));
Console.WriteLine(
"\nATTENTION : aucune suppression automatique.\nAnalyse uniquement.");
}
static Dictionary<string, FileInfo> GetInstallerFiles()
{
var dict = new Dictionary<string, FileInfo>(
StringComparer.OrdinalIgnoreCase);
try
{
foreach (string file in Directory.GetFiles(InstallerPath))
{
if (file.EndsWith(".msi", StringComparison.OrdinalIgnoreCase) ||
file.EndsWith(".msp", StringComparison.OrdinalIgnoreCase))
{
FileInfo fi = new FileInfo(file);
dict[fi.FullName] = fi;
}
}
}
catch
{
// Accès refusé
}
return dict;
}
static HashSet<string> GetReferencedInstallerFiles()
{
var result = new HashSet<string>(
StringComparer.OrdinalIgnoreCase);
ReadInstallerRegistry(
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData",
result);
ReadInstallerRegistry(
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Installer\UserData",
result);
return result;
}
static void ReadInstallerRegistry(string baseKey, HashSet<string> result)
{
try
{
using (RegistryKey root =
Registry.LocalMachine.OpenSubKey(baseKey))
{
if (root == null) return;
foreach (string sid in root.GetSubKeyNames())
{
using (RegistryKey products =
root.OpenSubKey(sid + @"\Products"))
{
if (products == null) continue;
foreach (string prod in products.GetSubKeyNames())
{
using (RegistryKey installProps =
products.OpenSubKey(prod + @"\InstallProperties"))
{
if (installProps == null) continue;
string localPackage =
installProps.GetValue("LocalPackage") as string;
if (!string.IsNullOrEmpty(localPackage))
result.Add(localPackage);
}
}
}
}
}
}
catch
{
// erreur registre
}
}
// ======================= PROGRESSION =======================
static void UpdateProgress(string currentDir)
{
spinnerIndex = (spinnerIndex + 1) % SpinnerChars.Length;
string dir = currentDir.Length > 50
? "..." + currentDir.Substring(currentDir.Length - 47)
: currentDir;
string line = string.Format("\r {0} {1:N0} fichiers | {2:N0} dossiers | {3} GB | {4}",
SpinnerChars[spinnerIndex],
TotalFilesScanned,
TotalFoldersScanned,
ToGB(TotalSizeScanned),
dir);
int width = 120;
try { width = Console.WindowWidth; } catch { }
if (line.Length < width)
line = line.PadRight(width - 1);
else if (line.Length >= width)
line = line.Substring(0, width - 1);
Console.Write(line);
}
static void ClearProgress()
{
int width = 120;
try { width = Console.WindowWidth; } catch { }
Console.Write("\r" + new string(' ', width - 1) + "\r");
}
// ======================= RESUME =======================
static void DisplaySummary()
{
Console.WriteLine("\n=== RESUME DU SCAN ===\n");
Console.WriteLine("Machine : {0}", Environment.MachineName);
Console.WriteLine("Duree : {0}", Timer.Elapsed.ToString(@"hh\:mm\:ss"));
Console.WriteLine("Fichiers analyses : {0:N0}", TotalFilesScanned);
Console.WriteLine("Dossiers analyses : {0:N0}", TotalFoldersScanned);
Console.WriteLine("Taille totale scannee : {0} GB", ToGB(TotalSizeScanned));
Console.WriteLine("Erreurs (acces refuse) : {0:N0}", TotalErrorCount);
}
// ======================= ARBORESCENCE RACINE =======================
static void DisplayRootBreakdown()
{
Console.WriteLine("\n=== REPARTITION RACINE (top 30) ===\n");
foreach (var item in RootBreakdown.OrderByDescending(r => r.Size).Take(30))
{
string name = Path.GetFileName(item.Path);
if (string.IsNullOrEmpty(name)) name = item.Path;
double pct = TotalSizeScanned > 0
? (item.Size * 100.0 / TotalSizeScanned)
: 0;
int barLen = Math.Min((int)(pct / 2), 25);
string bar = new string('#', barLen).PadRight(25);
Console.WriteLine("{0,-40} {1,8} GB [{2}] {3,5:F1}%",
name.Length > 40 ? name.Substring(0, 37) + "..." : name,
ToGB(item.Size),
bar,
pct);
}
}
// ======================= FICHIERS ANCIENS =======================
static void TrackStaleFiles(FileInfo fi)
{
try
{
if (fi.Length < 100 * ONE_MB) return;
if (fi.LastWriteTime > DateTime.Now.AddYears(-2)) return;
StaleFiles.Add(fi);
StaleFiles = StaleFiles
.OrderByDescending(f => f.Length)
.Take(20)
.ToList();
}
catch { }
}
static void DisplayStaleFiles()
{
if (StaleFiles.Count == 0) return;
Console.WriteLine("\n=== GROS FICHIERS ANCIENS (>100 Mo, non modifies depuis 2+ ans) ===\n");
foreach (var fi in StaleFiles)
{
string path = fi.FullName.Length > 75
? "..." + fi.FullName.Substring(fi.FullName.Length - 72)
: fi.FullName;
Console.WriteLine("{0,-75} {1,8} GB {2:yyyy-MM-dd}",
path, ToGB(fi.Length), fi.LastWriteTime);
}
}
// ======================= DOSSIERS TEMPORAIRES =======================
static void DisplayCacheFolders()
{
if (DetectedCacheFolders.Count == 0) return;
Console.WriteLine("\n=== DOSSIERS TEMPORAIRES / LOGS DETECTES (>50 Mo) ===\n");
long total = 0;
foreach (var cf in DetectedCacheFolders.OrderByDescending(c => c.Size).Take(30))
{
string path = cf.Path.Length > 70
? "..." + cf.Path.Substring(cf.Path.Length - 67)
: cf.Path;
Console.WriteLine("{0,-70} {1,8} GB",
path, ToGB(cf.Size));
total += cf.Size;
}
Console.WriteLine("\nTotal temporaires/logs : {0} GB", ToGB(total));
}
// ======================= DIAGNOSTIC SYSTEME =======================
static void DisplaySystemDiagnostic()
{
Console.WriteLine("\n=== DIAGNOSTIC ZONES SYSTEME ===\n");
var zones = new List<string>
{
@"C:\Windows\SoftwareDistribution\Download",
@"C:\Windows\Temp",
@"C:\Windows\Logs",
@"C:\Windows\Panther",
@"C:\ProgramData\Microsoft\Windows\WER",
@"C:\$Recycle.Bin"
};
// Temp + cache par profil utilisateur
try
{
foreach (string userDir in Directory.GetDirectories(@"C:\Users"))
{
string userName = Path.GetFileName(userDir);
if (userName.Equals("Public", StringComparison.OrdinalIgnoreCase)
|| userName.Equals("Default", StringComparison.OrdinalIgnoreCase)
|| userName.Equals("Default User", StringComparison.OrdinalIgnoreCase))
continue;
zones.Add(Path.Combine(userDir, @"AppData\Local\Temp"));
zones.Add(Path.Combine(userDir, @"AppData\Local\Microsoft\Windows\INetCache"));
// Firefox cache
string firefoxProfiles = Path.Combine(userDir, @"AppData\Local\Mozilla\Firefox\Profiles");
if (Directory.Exists(firefoxProfiles))
{
foreach (string profile in Directory.GetDirectories(firefoxProfiles))
zones.Add(Path.Combine(profile, "cache2"));
}
}
}
catch { }
// memory.dmp
string memDump = @"C:\Windows\memory.dmp";
if (File.Exists(memDump))
{
long dumpSize = new FileInfo(memDump).Length;
SystemDiagResults.Add(new LargeFolder { Path = memDump, Size = dumpSize });
Console.WriteLine("{0,-65} {1,8} GB", memDump, ToGB(dumpSize));
}
long grandTotal = 0;
foreach (string zone in zones)
{
if (!Directory.Exists(zone)) continue;
long size = GetDirectorySize(zone);
if (size < ONE_MB) continue;
SystemDiagResults.Add(new LargeFolder { Path = zone, Size = size });
Console.WriteLine("{0,-65} {1,8} GB",
zone.Length > 65 ? "..." + zone.Substring(zone.Length - 62) : zone,
ToGB(size));
grandTotal += size;
}
Console.WriteLine("\nTotal zones systeme recuperable : {0} GB", ToGB(grandTotal));
}
static long GetDirectorySize(string path)
{
long size = 0;
try
{
foreach (string file in Directory.GetFiles(path))
{
try { size += new FileInfo(file).Length; } catch { }
}
foreach (string dir in Directory.GetDirectories(path))
{
size += GetDirectorySize(dir);
}
}
catch { }
return size;
}
// ======================= EXPORT =======================
static void ExportReport(string rootPath)
{
string fileName = Environment.MachineName + ".diskusage.log";
string exeDir = AppDomain.CurrentDomain.BaseDirectory;
string exportPath = Path.Combine(exeDir, fileName);
try
{
var sb = new StringBuilder();
sb.AppendLine("DiskUsageLog - Rapport d'analyse disque");
sb.AppendLine("Machine : " + Environment.MachineName);
sb.AppendLine("Racine : " + rootPath);
sb.AppendLine("Date : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
sb.AppendLine("Duree : " + Timer.Elapsed.ToString(@"hh\:mm\:ss"));
sb.AppendLine(new string('=', 80));
sb.AppendFormat("\nFichiers analyses : {0:N0}\n", TotalFilesScanned);
sb.AppendFormat("Dossiers analyses : {0:N0}\n", TotalFoldersScanned);
sb.AppendFormat("Taille totale : {0} GB\n", ToGB(TotalSizeScanned));
sb.AppendFormat("Erreurs : {0:N0}\n", TotalErrorCount);
sb.AppendLine("\n--- REPARTITION RACINE ---");
foreach (var item in RootBreakdown.OrderByDescending(r => r.Size).Take(30))
{
string name = Path.GetFileName(item.Path);
if (string.IsNullOrEmpty(name)) name = item.Path;
sb.AppendFormat("{0,-60} {1} GB\n", name, ToGB(item.Size));
}
sb.AppendLine("\n--- DOSSIERS > 1 Go ---");
foreach (var f in LargeFolders.OrderByDescending(f => f.Size))
{
string flag = f.IsRiskArea ? " !!!" : "";
sb.AppendFormat("{0} : {1} GB{2}\n", f.Path, ToGB(f.Size), flag);
}
sb.AppendLine("\n--- TOP 20 EXTENSIONS ---");
foreach (var ext in ExtensionStats.OrderByDescending(e => e.Value.Size).Take(20))
{
sb.AppendFormat("{0,-12} {1,10} fichiers {2} GB\n",
ext.Key, ext.Value.Count, ToGB(ext.Value.Size));
}
sb.AppendLine("\n--- TOP 10 PLUS GROS FICHIERS ---");
foreach (var fi in LargestFiles)
sb.AppendFormat("{0} : {1} GB\n", fi.FullName, ToGB(fi.Length));
if (StaleFiles.Count > 0)
{
sb.AppendLine("\n--- GROS FICHIERS ANCIENS ---");
foreach (var fi in StaleFiles)
sb.AppendFormat("{0} : {1} GB (modifie {2:yyyy-MM-dd})\n",
fi.FullName, ToGB(fi.Length), fi.LastWriteTime);
}
if (DetectedCacheFolders.Count > 0)
{
sb.AppendLine("\n--- DOSSIERS TEMPORAIRES / LOGS ---");
foreach (var cf in DetectedCacheFolders.OrderByDescending(c => c.Size).Take(30))
sb.AppendFormat("{0} : {1} GB\n", cf.Path, ToGB(cf.Size));
}
if (SystemDiagResults.Count > 0)
{
sb.AppendLine("\n--- DIAGNOSTIC ZONES SYSTEME ---");
foreach (var sd in SystemDiagResults.OrderByDescending(s => s.Size))
sb.AppendFormat("{0} : {1} GB\n", sd.Path, ToGB(sd.Size));
}
File.WriteAllText(exportPath, sb.ToString());
Console.WriteLine("\nRapport exporte : " + exportPath);
}
catch (Exception ex)
{
Console.WriteLine("\nErreur export : " + ex.Message);
}
}
// ======================= UTILS =======================
static double ToGB(long bytes)
{
return Math.Round(bytes / 1024d / 1024 / 1024, 2);
}
static void DisplayXstoreInfo()
{
try
{
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\GFI"))
{
if (key == null) return;
Console.WriteLine(" --- XSTORE ---");
foreach (var name in key.GetValueNames())
{
var val = key.GetValue(name);
if (val != null && val.ToString().Length > 0)
Console.WriteLine($" {name,-25}: {val}");
}
}
}
catch { }
}
}
// ======================= MODELES =======================
class ExtensionStat
{
public int Count;
public long Size;
}
class LargeFolder
{
public string Path;
public long Size;
public bool IsRiskArea;
}
class CacheFolder
{
public string Path;
public long Size;
public string FolderName;
}
}
| 1 | using System; |
| 2 | using System.Collections.Generic; |
| 3 | using System.Diagnostics; |
| 4 | using System.IO; |
| 5 | using System.Linq; |
| 6 | using System.Text; |
| 7 | using Microsoft.Win32; |
| 8 | |
| 9 | namespace DiskUsageLog |
| 10 | { |
| 11 | class Program |
| 12 | { |
| 13 | const long ONE_GB = 1024L * 1024 * 1024; |
| 14 | const long ONE_MB = 1024L * 1024; |
| 15 | |
| 16 | static readonly string[] HardExcludedFolders = |
| 17 | { |
| 18 | "WinSxS", |
| 19 | "Microsoft.NET", |
| 20 | "assembly" |
| 21 | }; |
| 22 | |
| 23 | static readonly string InstallerPath = @"C:\Windows\Installer"; |
| 24 | |
| 25 | static readonly string[] KnownCacheFolderNames = |
| 26 | { |
| 27 | "Temp", "tmp", "Logs", "LogFiles", |
| 28 | "INetCache", "Temporary Internet Files", |
| 29 | "WER", "CrashDumps" |
| 30 | }; |
| 31 | |
| 32 | static Dictionary<string, ExtensionStat> ExtensionStats = |
| 33 | new Dictionary<string, ExtensionStat>(StringComparer.OrdinalIgnoreCase); |
| 34 | |
| 35 | static List<FileInfo> LargestFiles = new List<FileInfo>(); |
| 36 | static List<LargeFolder> LargeFolders = new List<LargeFolder>(); |
| 37 | static List<FileInfo> StaleFiles = new List<FileInfo>(); |
| 38 | static List<LargeFolder> RootBreakdown = new List<LargeFolder>(); |
| 39 | static List<CacheFolder> DetectedCacheFolders = new List<CacheFolder>(); |
| 40 | static List<LargeFolder> SystemDiagResults = new List<LargeFolder>(); |
| 41 | |
| 42 | static long TotalFilesScanned; |
| 43 | static long TotalFoldersScanned; |
| 44 | static long TotalErrorCount; |
| 45 | static long TotalSizeScanned; |
| 46 | |
| 47 | static Stopwatch Timer = new Stopwatch(); |
| 48 | static int spinnerIndex; |
| 49 | static readonly char[] SpinnerChars = { '|', '/', '-', '\\' }; |
| 50 | |
| 51 | static void Main(string[] args) |
| 52 | { |
| 53 | Console.OutputEncoding = System.Text.Encoding.UTF8; |
| 54 | |
| 55 | string rootPath = args.Length > 0 ? args[0] : @"C:\"; |
| 56 | |
| 57 | if (!Directory.Exists(rootPath)) |
| 58 | { |
| 59 | Console.WriteLine("Chemin invalide."); |
| 60 | return; |
| 61 | } |
| 62 | |
| 63 | Console.WriteLine("DiskUsageLog - Analyse disque"); |
| 64 | Console.WriteLine("Racine analysée : " + rootPath); |
| 65 | DisplayXstoreInfo(); |
| 66 | Console.WriteLine("--------------------------------------------------\n"); |
| 67 | |
| 68 | Timer.Start(); |
| 69 | |
| 70 | string[] rootDirs; |
| 71 | try { rootDirs = Directory.GetDirectories(rootPath); } |
| 72 | catch { rootDirs = new string[0]; } |
| 73 | |
| 74 | foreach (string dir in rootDirs) |
| 75 | { |
| 76 | long size = AnalyzeDirectory(dir); |
| 77 | RootBreakdown.Add(new LargeFolder |
| 78 | { |
| 79 | Path = dir, |
| 80 | Size = size |
| 81 | }); |
| 82 | } |
| 83 | |
| 84 | ClearProgress(); |
| 85 | Timer.Stop(); |
| 86 | |
| 87 | DisplaySummary(); |
| 88 | DisplayRootBreakdown(); |
| 89 | DisplayLargeFolders(); |
| 90 | DisplayTopExtensions(); |
| 91 | DisplayLargestFiles(); |
| 92 | DisplayStaleFiles(); |
| 93 | DisplayCacheFolders(); |
| 94 | DisplaySystemDiagnostic(); |
| 95 | DisplayOrphanInstallers(); |
| 96 | |
| 97 | ExportReport(rootPath); |
| 98 | |
| 99 | Console.WriteLine("\nAnalyse terminée."); |
| 100 | } |
| 101 | |
| 102 | // ======================= ANALYSE ======================= |
| 103 | |
| 104 | static long AnalyzeDirectory(string path) |
| 105 | { |
| 106 | long totalSize = 0; |
| 107 | TotalFoldersScanned++; |
| 108 | |
| 109 | string folderName = Path.GetFileName(path); |
| 110 | bool isKnownCache = KnownCacheFolderNames.Any(c => |
| 111 | c.Equals(folderName, StringComparison.OrdinalIgnoreCase)); |
| 112 | |
| 113 | try |
| 114 | { |
| 115 | foreach (string file in Directory.GetFiles(path)) |
| 116 | { |
| 117 | FileInfo fi = new FileInfo(file); |
| 118 | totalSize += fi.Length; |
| 119 | TotalFilesScanned++; |
| 120 | TotalSizeScanned += fi.Length; |
| 121 | |
| 122 | bool excluded = IsExcludedFile(fi, path); |
| 123 | |
| 124 | if (!IsSystemCritical(path) && !excluded) |
| 125 | TrackExtension(fi); |
| 126 | |
| 127 | if (!excluded) |
| 128 | { |
| 129 | TrackLargestFiles(fi); |
| 130 | TrackStaleFiles(fi); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | if (TotalFoldersScanned % 100 == 0) |
| 135 | UpdateProgress(path); |
| 136 | |
| 137 | foreach (string dir in Directory.GetDirectories(path)) |
| 138 | { |
| 139 | if (IsHardExcluded(dir)) continue; |
| 140 | totalSize += AnalyzeDirectory(dir); |
| 141 | } |
| 142 | } |
| 143 | catch |
| 144 | { |
| 145 | TotalErrorCount++; |
| 146 | } |
| 147 | |
| 148 | if (isKnownCache && totalSize >= 50 * ONE_MB) |
| 149 | { |
| 150 | DetectedCacheFolders.Add(new CacheFolder |
| 151 | { |
| 152 | Path = path, |
| 153 | Size = totalSize, |
| 154 | FolderName = folderName |
| 155 | }); |
| 156 | } |
| 157 | |
| 158 | if (totalSize >= ONE_GB) |
| 159 | { |
| 160 | LargeFolders.Add(new LargeFolder |
| 161 | { |
| 162 | Path = path, |
| 163 | Size = totalSize, |
| 164 | IsRiskArea = IsRiskArea(path) |
| 165 | }); |
| 166 | } |
| 167 | |
| 168 | return totalSize; |
| 169 | } |
| 170 | |
| 171 | // ======================= REGLES ======================= |
| 172 | |
| 173 | static bool IsHardExcluded(string path) |
| 174 | { |
| 175 | string name = Path.GetFileName(path); |
| 176 | return HardExcludedFolders.Any(e => |
| 177 | e.Equals(name, StringComparison.OrdinalIgnoreCase)); |
| 178 | } |
| 179 | |
| 180 | static bool IsSystemCritical(string path) |
| 181 | { |
| 182 | return path.StartsWith(@"C:\Windows\System32", |
| 183 | StringComparison.OrdinalIgnoreCase); |
| 184 | } |
| 185 | |
| 186 | static bool IsRiskArea(string path) |
| 187 | { |
| 188 | return path.StartsWith(@"C:\Windows\Installer", StringComparison.OrdinalIgnoreCase) |
| 189 | || path.StartsWith(@"C:\Windows\Temp", StringComparison.OrdinalIgnoreCase) |
| 190 | || path.StartsWith(@"C:\Windows\SoftwareDistribution", StringComparison.OrdinalIgnoreCase) |
| 191 | || path.Contains(@"\AppData\Local\Temp"); |
| 192 | } |
| 193 | |
| 194 | // ======================= EXCLUSIONS ======================= |
| 195 | |
| 196 | static bool IsExcludedFile(FileInfo fi, string dirPath) |
| 197 | { |
| 198 | if (fi.Extension.Equals(".dbf", StringComparison.OrdinalIgnoreCase) |
| 199 | && dirPath.StartsWith(@"C:\xstoredb", StringComparison.OrdinalIgnoreCase)) |
| 200 | return true; |
| 201 | |
| 202 | return false; |
| 203 | } |
| 204 | |
| 205 | // ======================= EXTENSIONS ======================= |
| 206 | |
| 207 | static void TrackExtension(FileInfo fi) |
| 208 | { |
| 209 | string ext = string.IsNullOrEmpty(fi.Extension) |
| 210 | ? "[sans_ext]" |
| 211 | : fi.Extension.ToLower(); |
| 212 | |
| 213 | if (!ExtensionStats.ContainsKey(ext)) |
| 214 | ExtensionStats[ext] = new ExtensionStat(); |
| 215 | |
| 216 | ExtensionStats[ext].Count++; |
| 217 | ExtensionStats[ext].Size += fi.Length; |
| 218 | } |
| 219 | |
| 220 | static void DisplayTopExtensions() |
| 221 | { |
| 222 | Console.WriteLine("\n=== TOP 20 EXTENSIONS (hors système critique) ===\n"); |
| 223 | Console.WriteLine("{0,-12} {1,10} {2,15}", |
| 224 | "Extension", "Fichiers", "Taille (GB)"); |
| 225 | |
| 226 | foreach (var ext in ExtensionStats |
| 227 | .OrderByDescending(e => e.Value.Size) |
| 228 | .Take(20)) |
| 229 | { |
| 230 | Console.WriteLine("{0,-12} {1,10} {2,15}", |
| 231 | ext.Key, |
| 232 | ext.Value.Count, |
| 233 | ToGB(ext.Value.Size)); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | // ======================= GROS FICHIERS ======================= |
| 238 | |
| 239 | static void TrackLargestFiles(FileInfo fi) |
| 240 | { |
| 241 | LargestFiles.Add(fi); |
| 242 | LargestFiles = LargestFiles |
| 243 | .OrderByDescending(f => f.Length) |
| 244 | .Take(10) |
| 245 | .ToList(); |
| 246 | } |
| 247 | |
| 248 | static void DisplayLargestFiles() |
| 249 | { |
| 250 | Console.WriteLine("\n=== TOP 10 PLUS GROS FICHIERS ===\n"); |
| 251 | |
| 252 | foreach (FileInfo fi in LargestFiles) |
| 253 | { |
| 254 | Console.WriteLine("{0} : {1} GB", |
| 255 | fi.FullName, |
| 256 | ToGB(fi.Length)); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | // ======================= DOSSIERS ======================= |
| 261 | |
| 262 | static void DisplayLargeFolders() |
| 263 | { |
| 264 | Console.WriteLine("\n=== DOSSIERS > 1 Go ===\n"); |
| 265 | |
| 266 | foreach (LargeFolder f in LargeFolders |
| 267 | .OrderByDescending(f => f.Size)) |
| 268 | { |
| 269 | string flag = f.IsRiskArea ? " !!!" : ""; |
| 270 | Console.WriteLine("{0} : {1} GB{2}", |
| 271 | f.Path, |
| 272 | ToGB(f.Size), |
| 273 | flag); |
| 274 | } |
| 275 | |
| 276 | Console.WriteLine("\n!!! = zone connue à dérive disque"); |
| 277 | } |
| 278 | |
| 279 | // ======================= MSI ORPHELINS ======================= |
| 280 | |
| 281 | static void DisplayOrphanInstallers() |
| 282 | { |
| 283 | Console.WriteLine("\n=== MSI / MSP ORPHELINS POTENTIELS ===\n"); |
| 284 | |
| 285 | if (!Directory.Exists(InstallerPath)) |
| 286 | { |
| 287 | Console.WriteLine("Répertoire Windows Installer introuvable."); |
| 288 | return; |
| 289 | } |
| 290 | |
| 291 | var allInstallers = GetInstallerFiles(); |
| 292 | var referencedInstallers = GetReferencedInstallerFiles(); |
| 293 | |
| 294 | var orphans = allInstallers.Values |
| 295 | .Where(f => !referencedInstallers.Contains(f.FullName)) |
| 296 | .OrderByDescending(f => f.Length) |
| 297 | .ToList(); |
| 298 | |
| 299 | long total = 0; |
| 300 | |
| 301 | foreach (var fi in orphans) |
| 302 | { |
| 303 | Console.WriteLine("{0,-20} {1,10} GB", |
| 304 | fi.Name, |
| 305 | ToGB(fi.Length)); |
| 306 | |
| 307 | total += fi.Length; |
| 308 | } |
| 309 | |
| 310 | Console.WriteLine("\nTotal récupérable potentiel : {0} GB", |
| 311 | ToGB(total)); |
| 312 | |
| 313 | Console.WriteLine( |
| 314 | "\nATTENTION : aucune suppression automatique.\nAnalyse uniquement."); |
| 315 | } |
| 316 | |
| 317 | static Dictionary<string, FileInfo> GetInstallerFiles() |
| 318 | { |
| 319 | var dict = new Dictionary<string, FileInfo>( |
| 320 | StringComparer.OrdinalIgnoreCase); |
| 321 | |
| 322 | try |
| 323 | { |
| 324 | foreach (string file in Directory.GetFiles(InstallerPath)) |
| 325 | { |
| 326 | if (file.EndsWith(".msi", StringComparison.OrdinalIgnoreCase) || |
| 327 | file.EndsWith(".msp", StringComparison.OrdinalIgnoreCase)) |
| 328 | { |
| 329 | FileInfo fi = new FileInfo(file); |
| 330 | dict[fi.FullName] = fi; |
| 331 | } |
| 332 | } |
| 333 | } |
| 334 | catch |
| 335 | { |
| 336 | // Accès refusé |
| 337 | } |
| 338 | |
| 339 | return dict; |
| 340 | } |
| 341 | |
| 342 | static HashSet<string> GetReferencedInstallerFiles() |
| 343 | { |
| 344 | var result = new HashSet<string>( |
| 345 | StringComparer.OrdinalIgnoreCase); |
| 346 | |
| 347 | ReadInstallerRegistry( |
| 348 | @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData", |
| 349 | result); |
| 350 | |
| 351 | ReadInstallerRegistry( |
| 352 | @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Installer\UserData", |
| 353 | result); |
| 354 | |
| 355 | return result; |
| 356 | } |
| 357 | |
| 358 | static void ReadInstallerRegistry(string baseKey, HashSet<string> result) |
| 359 | { |
| 360 | try |
| 361 | { |
| 362 | using (RegistryKey root = |
| 363 | Registry.LocalMachine.OpenSubKey(baseKey)) |
| 364 | { |
| 365 | if (root == null) return; |
| 366 | |
| 367 | foreach (string sid in root.GetSubKeyNames()) |
| 368 | { |
| 369 | using (RegistryKey products = |
| 370 | root.OpenSubKey(sid + @"\Products")) |
| 371 | { |
| 372 | if (products == null) continue; |
| 373 | |
| 374 | foreach (string prod in products.GetSubKeyNames()) |
| 375 | { |
| 376 | using (RegistryKey installProps = |
| 377 | products.OpenSubKey(prod + @"\InstallProperties")) |
| 378 | { |
| 379 | if (installProps == null) continue; |
| 380 | |
| 381 | string localPackage = |
| 382 | installProps.GetValue("LocalPackage") as string; |
| 383 | |
| 384 | if (!string.IsNullOrEmpty(localPackage)) |
| 385 | result.Add(localPackage); |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | catch |
| 393 | { |
| 394 | // erreur registre |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | // ======================= PROGRESSION ======================= |
| 399 | |
| 400 | static void UpdateProgress(string currentDir) |
| 401 | { |
| 402 | spinnerIndex = (spinnerIndex + 1) % SpinnerChars.Length; |
| 403 | string dir = currentDir.Length > 50 |
| 404 | ? "..." + currentDir.Substring(currentDir.Length - 47) |
| 405 | : currentDir; |
| 406 | |
| 407 | string line = string.Format("\r {0} {1:N0} fichiers | {2:N0} dossiers | {3} GB | {4}", |
| 408 | SpinnerChars[spinnerIndex], |
| 409 | TotalFilesScanned, |
| 410 | TotalFoldersScanned, |
| 411 | ToGB(TotalSizeScanned), |
| 412 | dir); |
| 413 | |
| 414 | int width = 120; |
| 415 | try { width = Console.WindowWidth; } catch { } |
| 416 | if (line.Length < width) |
| 417 | line = line.PadRight(width - 1); |
| 418 | else if (line.Length >= width) |
| 419 | line = line.Substring(0, width - 1); |
| 420 | |
| 421 | Console.Write(line); |
| 422 | } |
| 423 | |
| 424 | static void ClearProgress() |
| 425 | { |
| 426 | int width = 120; |
| 427 | try { width = Console.WindowWidth; } catch { } |
| 428 | Console.Write("\r" + new string(' ', width - 1) + "\r"); |
| 429 | } |
| 430 | |
| 431 | // ======================= RESUME ======================= |
| 432 | |
| 433 | static void DisplaySummary() |
| 434 | { |
| 435 | Console.WriteLine("\n=== RESUME DU SCAN ===\n"); |
| 436 | Console.WriteLine("Machine : {0}", Environment.MachineName); |
| 437 | Console.WriteLine("Duree : {0}", Timer.Elapsed.ToString(@"hh\:mm\:ss")); |
| 438 | Console.WriteLine("Fichiers analyses : {0:N0}", TotalFilesScanned); |
| 439 | Console.WriteLine("Dossiers analyses : {0:N0}", TotalFoldersScanned); |
| 440 | Console.WriteLine("Taille totale scannee : {0} GB", ToGB(TotalSizeScanned)); |
| 441 | Console.WriteLine("Erreurs (acces refuse) : {0:N0}", TotalErrorCount); |
| 442 | } |
| 443 | |
| 444 | // ======================= ARBORESCENCE RACINE ======================= |
| 445 | |
| 446 | static void DisplayRootBreakdown() |
| 447 | { |
| 448 | Console.WriteLine("\n=== REPARTITION RACINE (top 30) ===\n"); |
| 449 | |
| 450 | foreach (var item in RootBreakdown.OrderByDescending(r => r.Size).Take(30)) |
| 451 | { |
| 452 | string name = Path.GetFileName(item.Path); |
| 453 | if (string.IsNullOrEmpty(name)) name = item.Path; |
| 454 | |
| 455 | double pct = TotalSizeScanned > 0 |
| 456 | ? (item.Size * 100.0 / TotalSizeScanned) |
| 457 | : 0; |
| 458 | |
| 459 | int barLen = Math.Min((int)(pct / 2), 25); |
| 460 | string bar = new string('#', barLen).PadRight(25); |
| 461 | |
| 462 | Console.WriteLine("{0,-40} {1,8} GB [{2}] {3,5:F1}%", |
| 463 | name.Length > 40 ? name.Substring(0, 37) + "..." : name, |
| 464 | ToGB(item.Size), |
| 465 | bar, |
| 466 | pct); |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | // ======================= FICHIERS ANCIENS ======================= |
| 471 | |
| 472 | static void TrackStaleFiles(FileInfo fi) |
| 473 | { |
| 474 | try |
| 475 | { |
| 476 | if (fi.Length < 100 * ONE_MB) return; |
| 477 | if (fi.LastWriteTime > DateTime.Now.AddYears(-2)) return; |
| 478 | |
| 479 | StaleFiles.Add(fi); |
| 480 | StaleFiles = StaleFiles |
| 481 | .OrderByDescending(f => f.Length) |
| 482 | .Take(20) |
| 483 | .ToList(); |
| 484 | } |
| 485 | catch { } |
| 486 | } |
| 487 | |
| 488 | static void DisplayStaleFiles() |
| 489 | { |
| 490 | if (StaleFiles.Count == 0) return; |
| 491 | |
| 492 | Console.WriteLine("\n=== GROS FICHIERS ANCIENS (>100 Mo, non modifies depuis 2+ ans) ===\n"); |
| 493 | |
| 494 | foreach (var fi in StaleFiles) |
| 495 | { |
| 496 | string path = fi.FullName.Length > 75 |
| 497 | ? "..." + fi.FullName.Substring(fi.FullName.Length - 72) |
| 498 | : fi.FullName; |
| 499 | |
| 500 | Console.WriteLine("{0,-75} {1,8} GB {2:yyyy-MM-dd}", |
| 501 | path, ToGB(fi.Length), fi.LastWriteTime); |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | // ======================= DOSSIERS TEMPORAIRES ======================= |
| 506 | |
| 507 | static void DisplayCacheFolders() |
| 508 | { |
| 509 | if (DetectedCacheFolders.Count == 0) return; |
| 510 | |
| 511 | Console.WriteLine("\n=== DOSSIERS TEMPORAIRES / LOGS DETECTES (>50 Mo) ===\n"); |
| 512 | |
| 513 | long total = 0; |
| 514 | foreach (var cf in DetectedCacheFolders.OrderByDescending(c => c.Size).Take(30)) |
| 515 | { |
| 516 | string path = cf.Path.Length > 70 |
| 517 | ? "..." + cf.Path.Substring(cf.Path.Length - 67) |
| 518 | : cf.Path; |
| 519 | |
| 520 | Console.WriteLine("{0,-70} {1,8} GB", |
| 521 | path, ToGB(cf.Size)); |
| 522 | total += cf.Size; |
| 523 | } |
| 524 | |
| 525 | Console.WriteLine("\nTotal temporaires/logs : {0} GB", ToGB(total)); |
| 526 | } |
| 527 | |
| 528 | // ======================= DIAGNOSTIC SYSTEME ======================= |
| 529 | |
| 530 | static void DisplaySystemDiagnostic() |
| 531 | { |
| 532 | Console.WriteLine("\n=== DIAGNOSTIC ZONES SYSTEME ===\n"); |
| 533 | |
| 534 | var zones = new List<string> |
| 535 | { |
| 536 | @"C:\Windows\SoftwareDistribution\Download", |
| 537 | @"C:\Windows\Temp", |
| 538 | @"C:\Windows\Logs", |
| 539 | @"C:\Windows\Panther", |
| 540 | @"C:\ProgramData\Microsoft\Windows\WER", |
| 541 | @"C:\$Recycle.Bin" |
| 542 | }; |
| 543 | |
| 544 | // Temp + cache par profil utilisateur |
| 545 | try |
| 546 | { |
| 547 | foreach (string userDir in Directory.GetDirectories(@"C:\Users")) |
| 548 | { |
| 549 | string userName = Path.GetFileName(userDir); |
| 550 | if (userName.Equals("Public", StringComparison.OrdinalIgnoreCase) |
| 551 | || userName.Equals("Default", StringComparison.OrdinalIgnoreCase) |
| 552 | || userName.Equals("Default User", StringComparison.OrdinalIgnoreCase)) |
| 553 | continue; |
| 554 | |
| 555 | zones.Add(Path.Combine(userDir, @"AppData\Local\Temp")); |
| 556 | zones.Add(Path.Combine(userDir, @"AppData\Local\Microsoft\Windows\INetCache")); |
| 557 | |
| 558 | // Firefox cache |
| 559 | string firefoxProfiles = Path.Combine(userDir, @"AppData\Local\Mozilla\Firefox\Profiles"); |
| 560 | if (Directory.Exists(firefoxProfiles)) |
| 561 | { |
| 562 | foreach (string profile in Directory.GetDirectories(firefoxProfiles)) |
| 563 | zones.Add(Path.Combine(profile, "cache2")); |
| 564 | } |
| 565 | } |
| 566 | } |
| 567 | catch { } |
| 568 | |
| 569 | // memory.dmp |
| 570 | string memDump = @"C:\Windows\memory.dmp"; |
| 571 | if (File.Exists(memDump)) |
| 572 | { |
| 573 | long dumpSize = new FileInfo(memDump).Length; |
| 574 | SystemDiagResults.Add(new LargeFolder { Path = memDump, Size = dumpSize }); |
| 575 | Console.WriteLine("{0,-65} {1,8} GB", memDump, ToGB(dumpSize)); |
| 576 | } |
| 577 | |
| 578 | long grandTotal = 0; |
| 579 | foreach (string zone in zones) |
| 580 | { |
| 581 | if (!Directory.Exists(zone)) continue; |
| 582 | long size = GetDirectorySize(zone); |
| 583 | if (size < ONE_MB) continue; |
| 584 | |
| 585 | SystemDiagResults.Add(new LargeFolder { Path = zone, Size = size }); |
| 586 | Console.WriteLine("{0,-65} {1,8} GB", |
| 587 | zone.Length > 65 ? "..." + zone.Substring(zone.Length - 62) : zone, |
| 588 | ToGB(size)); |
| 589 | grandTotal += size; |
| 590 | } |
| 591 | |
| 592 | Console.WriteLine("\nTotal zones systeme recuperable : {0} GB", ToGB(grandTotal)); |
| 593 | } |
| 594 | |
| 595 | static long GetDirectorySize(string path) |
| 596 | { |
| 597 | long size = 0; |
| 598 | try |
| 599 | { |
| 600 | foreach (string file in Directory.GetFiles(path)) |
| 601 | { |
| 602 | try { size += new FileInfo(file).Length; } catch { } |
| 603 | } |
| 604 | foreach (string dir in Directory.GetDirectories(path)) |
| 605 | { |
| 606 | size += GetDirectorySize(dir); |
| 607 | } |
| 608 | } |
| 609 | catch { } |
| 610 | return size; |
| 611 | } |
| 612 | |
| 613 | // ======================= EXPORT ======================= |
| 614 | |
| 615 | static void ExportReport(string rootPath) |
| 616 | { |
| 617 | string fileName = Environment.MachineName + ".diskusage.log"; |
| 618 | string exeDir = AppDomain.CurrentDomain.BaseDirectory; |
| 619 | string exportPath = Path.Combine(exeDir, fileName); |
| 620 | |
| 621 | try |
| 622 | { |
| 623 | var sb = new StringBuilder(); |
| 624 | sb.AppendLine("DiskUsageLog - Rapport d'analyse disque"); |
| 625 | sb.AppendLine("Machine : " + Environment.MachineName); |
| 626 | sb.AppendLine("Racine : " + rootPath); |
| 627 | sb.AppendLine("Date : " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); |
| 628 | sb.AppendLine("Duree : " + Timer.Elapsed.ToString(@"hh\:mm\:ss")); |
| 629 | sb.AppendLine(new string('=', 80)); |
| 630 | |
| 631 | sb.AppendFormat("\nFichiers analyses : {0:N0}\n", TotalFilesScanned); |
| 632 | sb.AppendFormat("Dossiers analyses : {0:N0}\n", TotalFoldersScanned); |
| 633 | sb.AppendFormat("Taille totale : {0} GB\n", ToGB(TotalSizeScanned)); |
| 634 | sb.AppendFormat("Erreurs : {0:N0}\n", TotalErrorCount); |
| 635 | |
| 636 | sb.AppendLine("\n--- REPARTITION RACINE ---"); |
| 637 | foreach (var item in RootBreakdown.OrderByDescending(r => r.Size).Take(30)) |
| 638 | { |
| 639 | string name = Path.GetFileName(item.Path); |
| 640 | if (string.IsNullOrEmpty(name)) name = item.Path; |
| 641 | sb.AppendFormat("{0,-60} {1} GB\n", name, ToGB(item.Size)); |
| 642 | } |
| 643 | |
| 644 | sb.AppendLine("\n--- DOSSIERS > 1 Go ---"); |
| 645 | foreach (var f in LargeFolders.OrderByDescending(f => f.Size)) |
| 646 | { |
| 647 | string flag = f.IsRiskArea ? " !!!" : ""; |
| 648 | sb.AppendFormat("{0} : {1} GB{2}\n", f.Path, ToGB(f.Size), flag); |
| 649 | } |
| 650 | |
| 651 | sb.AppendLine("\n--- TOP 20 EXTENSIONS ---"); |
| 652 | foreach (var ext in ExtensionStats.OrderByDescending(e => e.Value.Size).Take(20)) |
| 653 | { |
| 654 | sb.AppendFormat("{0,-12} {1,10} fichiers {2} GB\n", |
| 655 | ext.Key, ext.Value.Count, ToGB(ext.Value.Size)); |
| 656 | } |
| 657 | |
| 658 | sb.AppendLine("\n--- TOP 10 PLUS GROS FICHIERS ---"); |
| 659 | foreach (var fi in LargestFiles) |
| 660 | sb.AppendFormat("{0} : {1} GB\n", fi.FullName, ToGB(fi.Length)); |
| 661 | |
| 662 | if (StaleFiles.Count > 0) |
| 663 | { |
| 664 | sb.AppendLine("\n--- GROS FICHIERS ANCIENS ---"); |
| 665 | foreach (var fi in StaleFiles) |
| 666 | sb.AppendFormat("{0} : {1} GB (modifie {2:yyyy-MM-dd})\n", |
| 667 | fi.FullName, ToGB(fi.Length), fi.LastWriteTime); |
| 668 | } |
| 669 | |
| 670 | if (DetectedCacheFolders.Count > 0) |
| 671 | { |
| 672 | sb.AppendLine("\n--- DOSSIERS TEMPORAIRES / LOGS ---"); |
| 673 | foreach (var cf in DetectedCacheFolders.OrderByDescending(c => c.Size).Take(30)) |
| 674 | sb.AppendFormat("{0} : {1} GB\n", cf.Path, ToGB(cf.Size)); |
| 675 | } |
| 676 | |
| 677 | if (SystemDiagResults.Count > 0) |
| 678 | { |
| 679 | sb.AppendLine("\n--- DIAGNOSTIC ZONES SYSTEME ---"); |
| 680 | foreach (var sd in SystemDiagResults.OrderByDescending(s => s.Size)) |
| 681 | sb.AppendFormat("{0} : {1} GB\n", sd.Path, ToGB(sd.Size)); |
| 682 | } |
| 683 | |
| 684 | File.WriteAllText(exportPath, sb.ToString()); |
| 685 | Console.WriteLine("\nRapport exporte : " + exportPath); |
| 686 | } |
| 687 | catch (Exception ex) |
| 688 | { |
| 689 | Console.WriteLine("\nErreur export : " + ex.Message); |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | // ======================= UTILS ======================= |
| 694 | |
| 695 | static double ToGB(long bytes) |
| 696 | { |
| 697 | return Math.Round(bytes / 1024d / 1024 / 1024, 2); |
| 698 | } |
| 699 | |
| 700 | static void DisplayXstoreInfo() |
| 701 | { |
| 702 | try |
| 703 | { |
| 704 | using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\GFI")) |
| 705 | { |
| 706 | if (key == null) return; |
| 707 | |
| 708 | Console.WriteLine(" --- XSTORE ---"); |
| 709 | |
| 710 | foreach (var name in key.GetValueNames()) |
| 711 | { |
| 712 | var val = key.GetValue(name); |
| 713 | if (val != null && val.ToString().Length > 0) |
| 714 | Console.WriteLine($" {name,-25}: {val}"); |
| 715 | } |
| 716 | } |
| 717 | } |
| 718 | catch { } |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | // ======================= MODELES ======================= |
| 723 | |
| 724 | class ExtensionStat |
| 725 | { |
| 726 | public int Count; |
| 727 | public long Size; |
| 728 | } |
| 729 | |
| 730 | class LargeFolder |
| 731 | { |
| 732 | public string Path; |
| 733 | public long Size; |
| 734 | public bool IsRiskArea; |
| 735 | } |
| 736 | |
| 737 | class CacheFolder |
| 738 | { |
| 739 | public string Path; |
| 740 | public long Size; |
| 741 | public string FolderName; |
| 742 | } |
| 743 | } |