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 ExtensionStats = new Dictionary(StringComparer.OrdinalIgnoreCase); static List LargestFiles = new List(); static List LargeFolders = new List(); static List StaleFiles = new List(); static List RootBreakdown = new List(); static List DetectedCacheFolders = new List(); static List SystemDiagResults = new List(); 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 GetInstallerFiles() { var dict = new Dictionary( 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 GetReferencedInstallerFiles() { var result = new HashSet( 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 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 { @"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; } }