using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Win32; namespace DiskUsageLog { class Program { const long ONE_GB = 1024L * 1024 * 1024; static readonly string[] HardExcludedFolders = { "WinSxS", "Microsoft.NET", "assembly" }; static readonly string InstallerPath = @"C:\Windows\Installer"; static Dictionary ExtensionStats = new Dictionary(StringComparer.OrdinalIgnoreCase); static List LargestFiles = new List(); static List LargeFolders = new List(); static void Main(string[] args) { 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); Console.WriteLine("--------------------------------------------------\n"); foreach (string dir in Directory.GetDirectories(rootPath)) { AnalyzeDirectory(dir); } DisplayLargeFolders(); DisplayTopExtensions(); DisplayLargestFiles(); DisplayOrphanInstallers(); Console.WriteLine("\nAnalyse terminée."); Console.ReadLine(); } // ======================= ANALYSE ======================= static long AnalyzeDirectory(string path) { long totalSize = 0; try { foreach (string file in Directory.GetFiles(path)) { FileInfo fi = new FileInfo(file); totalSize += fi.Length; if (!IsSystemCritical(path)) TrackExtension(fi); TrackLargestFiles(fi); } foreach (string dir in Directory.GetDirectories(path)) { if (IsHardExcluded(dir)) continue; totalSize += AnalyzeDirectory(dir); } } catch { // Accès refusé / erreur disque } 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"); } // ======================= 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 } } // ======================= UTILS ======================= static double ToGB(long bytes) { return Math.Round(bytes / 1024d / 1024 / 1024, 2); } } // ======================= MODELES ======================= class ExtensionStat { public int Count; public long Size; } class LargeFolder { public string Path; public long Size; public bool IsRiskArea; } }