using System.Security.Cryptography; namespace RedundancyFinder { public class Finder { Dictionary redundancies = new Dictionary(); public Dictionary Redundancies { get => redundancies; } string[] extensions; public event EventHandler? DirectoryError; public event EventHandler? FileError; public event EventHandler? FileFound; public event EventHandler? TaskStarted; public event EventHandler? ProcessingFile; public void FindRedundancies(string[] paths, string[] extensions) { this.extensions = extensions; foreach (var path in paths) { if (Directory.Exists(path)) { ProcessDirectory(path); } else if (File.Exists(path)) { ProcessFile(path); } } } private void ProcessDirectory(string directoryPath) { try { // Process files in the current directory foreach (var file in Directory.GetFiles(directoryPath)) { ProcessFile(file); } // Recursively process subdirectories foreach (var subDirectory in Directory.GetDirectories(directoryPath)) { // Check if the directory is hidden and skip it if true var attributes = File.GetAttributes(subDirectory); if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) { continue; } try { ProcessDirectory(subDirectory); } catch (Exception ex) { DirectoryError?.Invoke(this, new DirectoryErrorEventArgs() { Exception = ex, Path = subDirectory }); } } } catch (Exception ex) { DirectoryError?.Invoke(this, new DirectoryErrorEventArgs() { Exception = ex, Path = directoryPath }); } } private void ProcessFile(string filePath) { if (!extensions.Contains(Path.GetExtension(filePath))) { return; } TaskStarted?.Invoke(this, filePath); ProcessingFile?.Invoke(this, new ProcessingFileEventArgs() { Path = filePath }); try { var fileHash = ComputeFileHash(filePath); if (fileHash == null) { return; } long fileSize = new FileInfo(filePath).Length; lock (redundancies) { if (redundancies.ContainsKey(fileHash)) { return; } if (!redundancies.ContainsKey(fileHash)) { var redundancy = new Redundancy() { Hash = fileHash, FileSize = fileSize }; redundancies.Add(fileHash, redundancy); } redundancies[fileHash].Paths.Add(filePath); } FileFound?.Invoke(this, new FileFoundEventArgs(filePath, fileHash, fileSize)); } catch (Exception ex) { FileError?.Invoke(this, new FileErrorEventArgs() { Exception = ex, Path = filePath }); } } private static string ComputeFileHash(string filePath) { using var sha256 = SHA256.Create(); using var stream = File.OpenRead(filePath); var hash = sha256.ComputeHash(stream); return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); } } }