最後活躍 1777326157

修訂 260136e001d054ec715783dca67705604ff8b7de

gistfile1.txt 原始檔案
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Linq;
5using Microsoft.Win32;
6
7namespace DiskUsageLog
8{
9 class Program
10 {
11 const long ONE_GB = 1024L * 1024 * 1024;
12
13 static readonly string[] HardExcludedFolders =
14 {
15 "WinSxS",
16 "Microsoft.NET",
17 "assembly"
18 };
19
20 static readonly string InstallerPath = @"C:\Windows\Installer";
21
22 static Dictionary<string, ExtensionStat> ExtensionStats =
23 new Dictionary<string, ExtensionStat>(StringComparer.OrdinalIgnoreCase);
24
25 static List<FileInfo> LargestFiles = new List<FileInfo>();
26 static List<LargeFolder> LargeFolders = new List<LargeFolder>();
27
28 static void Main(string[] args)
29 {
30 string rootPath = args.Length > 0 ? args[0] : @"C:\";
31
32 if (!Directory.Exists(rootPath))
33 {
34 Console.WriteLine("Chemin invalide.");
35 return;
36 }
37
38 Console.WriteLine("DiskUsageLog - Analyse disque");
39 Console.WriteLine("Racine analysée : " + rootPath);
40 Console.WriteLine("--------------------------------------------------\n");
41
42 foreach (string dir in Directory.GetDirectories(rootPath))
43 {
44 AnalyzeDirectory(dir);
45 }
46
47 DisplayLargeFolders();
48 DisplayTopExtensions();
49 DisplayLargestFiles();
50 DisplayOrphanInstallers();
51
52 Console.WriteLine("\nAnalyse terminée.");
53 Console.ReadLine();
54 }
55
56 // ======================= ANALYSE =======================
57
58 static long AnalyzeDirectory(string path)
59 {
60 long totalSize = 0;
61
62 try
63 {
64 foreach (string file in Directory.GetFiles(path))
65 {
66 FileInfo fi = new FileInfo(file);
67 totalSize += fi.Length;
68
69 if (!IsSystemCritical(path))
70 TrackExtension(fi);
71
72 TrackLargestFiles(fi);
73 }
74
75 foreach (string dir in Directory.GetDirectories(path))
76 {
77 if (IsHardExcluded(dir)) continue;
78 totalSize += AnalyzeDirectory(dir);
79 }
80 }
81 catch
82 {
83 // Accès refusé / erreur disque
84 }
85
86 if (totalSize >= ONE_GB)
87 {
88 LargeFolders.Add(new LargeFolder
89 {
90 Path = path,
91 Size = totalSize,
92 IsRiskArea = IsRiskArea(path)
93 });
94 }
95
96 return totalSize;
97 }
98
99 // ======================= REGLES =======================
100
101 static bool IsHardExcluded(string path)
102 {
103 string name = Path.GetFileName(path);
104 return HardExcludedFolders.Any(e =>
105 e.Equals(name, StringComparison.OrdinalIgnoreCase));
106 }
107
108 static bool IsSystemCritical(string path)
109 {
110 return path.StartsWith(@"C:\Windows\System32",
111 StringComparison.OrdinalIgnoreCase);
112 }
113
114 static bool IsRiskArea(string path)
115 {
116 return path.StartsWith(@"C:\Windows\Installer", StringComparison.OrdinalIgnoreCase)
117 || path.StartsWith(@"C:\Windows\Temp", StringComparison.OrdinalIgnoreCase)
118 || path.StartsWith(@"C:\Windows\SoftwareDistribution", StringComparison.OrdinalIgnoreCase)
119 || path.Contains(@"\AppData\Local\Temp");
120 }
121
122 // ======================= EXTENSIONS =======================
123
124 static void TrackExtension(FileInfo fi)
125 {
126 string ext = string.IsNullOrEmpty(fi.Extension)
127 ? "[sans_ext]"
128 : fi.Extension.ToLower();
129
130 if (!ExtensionStats.ContainsKey(ext))
131 ExtensionStats[ext] = new ExtensionStat();
132
133 ExtensionStats[ext].Count++;
134 ExtensionStats[ext].Size += fi.Length;
135 }
136
137 static void DisplayTopExtensions()
138 {
139 Console.WriteLine("\n=== TOP 20 EXTENSIONS (hors système critique) ===\n");
140 Console.WriteLine("{0,-12} {1,10} {2,15}",
141 "Extension", "Fichiers", "Taille (GB)");
142
143 foreach (var ext in ExtensionStats
144 .OrderByDescending(e => e.Value.Size)
145 .Take(20))
146 {
147 Console.WriteLine("{0,-12} {1,10} {2,15}",
148 ext.Key,
149 ext.Value.Count,
150 ToGB(ext.Value.Size));
151 }
152 }
153
154 // ======================= GROS FICHIERS =======================
155
156 static void TrackLargestFiles(FileInfo fi)
157 {
158 LargestFiles.Add(fi);
159 LargestFiles = LargestFiles
160 .OrderByDescending(f => f.Length)
161 .Take(10)
162 .ToList();
163 }
164
165 static void DisplayLargestFiles()
166 {
167 Console.WriteLine("\n=== TOP 10 PLUS GROS FICHIERS ===\n");
168
169 foreach (FileInfo fi in LargestFiles)
170 {
171 Console.WriteLine("{0} : {1} GB",
172 fi.FullName,
173 ToGB(fi.Length));
174 }
175 }
176
177 // ======================= DOSSIERS =======================
178
179 static void DisplayLargeFolders()
180 {
181 Console.WriteLine("\n=== DOSSIERS > 1 Go ===\n");
182
183 foreach (LargeFolder f in LargeFolders
184 .OrderByDescending(f => f.Size))
185 {
186 string flag = f.IsRiskArea ? " !!!" : "";
187 Console.WriteLine("{0} : {1} GB{2}",
188 f.Path,
189 ToGB(f.Size),
190 flag);
191 }
192
193 Console.WriteLine("\n!!! = zone connue à dérive disque");
194 }
195
196 // ======================= MSI ORPHELINS =======================
197
198 static void DisplayOrphanInstallers()
199 {
200 Console.WriteLine("\n=== MSI / MSP ORPHELINS POTENTIELS ===\n");
201
202 if (!Directory.Exists(InstallerPath))
203 {
204 Console.WriteLine("Répertoire Windows Installer introuvable.");
205 return;
206 }
207
208 var allInstallers = GetInstallerFiles();
209 var referencedInstallers = GetReferencedInstallerFiles();
210
211 var orphans = allInstallers.Values
212 .Where(f => !referencedInstallers.Contains(f.FullName))
213 .OrderByDescending(f => f.Length)
214 .ToList();
215
216 long total = 0;
217
218 foreach (var fi in orphans)
219 {
220 Console.WriteLine("{0,-20} {1,10} GB",
221 fi.Name,
222 ToGB(fi.Length));
223
224 total += fi.Length;
225 }
226
227 Console.WriteLine("\nTotal récupérable potentiel : {0} GB",
228 ToGB(total));
229
230 Console.WriteLine(
231 "\nATTENTION : aucune suppression automatique.\nAnalyse uniquement.");
232 }
233
234 static Dictionary<string, FileInfo> GetInstallerFiles()
235 {
236 var dict = new Dictionary<string, FileInfo>(
237 StringComparer.OrdinalIgnoreCase);
238
239 try
240 {
241 foreach (string file in Directory.GetFiles(InstallerPath))
242 {
243 if (file.EndsWith(".msi", StringComparison.OrdinalIgnoreCase) ||
244 file.EndsWith(".msp", StringComparison.OrdinalIgnoreCase))
245 {
246 FileInfo fi = new FileInfo(file);
247 dict[fi.FullName] = fi;
248 }
249 }
250 }
251 catch
252 {
253 // Accès refusé
254 }
255
256 return dict;
257 }
258
259 static HashSet<string> GetReferencedInstallerFiles()
260 {
261 var result = new HashSet<string>(
262 StringComparer.OrdinalIgnoreCase);
263
264 ReadInstallerRegistry(
265 @"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData",
266 result);
267
268 ReadInstallerRegistry(
269 @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Installer\UserData",
270 result);
271
272 return result;
273 }
274
275 static void ReadInstallerRegistry(string baseKey, HashSet<string> result)
276 {
277 try
278 {
279 using (RegistryKey root =
280 Registry.LocalMachine.OpenSubKey(baseKey))
281 {
282 if (root == null) return;
283
284 foreach (string sid in root.GetSubKeyNames())
285 {
286 using (RegistryKey products =
287 root.OpenSubKey(sid + @"\Products"))
288 {
289 if (products == null) continue;
290
291 foreach (string prod in products.GetSubKeyNames())
292 {
293 using (RegistryKey installProps =
294 products.OpenSubKey(prod + @"\InstallProperties"))
295 {
296 if (installProps == null) continue;
297
298 string localPackage =
299 installProps.GetValue("LocalPackage") as string;
300
301 if (!string.IsNullOrEmpty(localPackage))
302 result.Add(localPackage);
303 }
304 }
305 }
306 }
307 }
308 }
309 catch
310 {
311 // erreur registre
312 }
313 }
314
315 // ======================= UTILS =======================
316
317 static double ToGB(long bytes)
318 {
319 return Math.Round(bytes / 1024d / 1024 / 1024, 2);
320 }
321 }
322
323 // ======================= MODELES =======================
324
325 class ExtensionStat
326 {
327 public int Count;
328 public long Size;
329 }
330
331 class LargeFolder
332 {
333 public string Path;
334 public long Size;
335 public bool IsRiskArea;
336 }
337}