diff --git a/mdfinder.hashprovider/IHashProvider.cs b/mdfinder.hashprovider/IHashProvider.cs new file mode 100644 index 0000000..72dfde4 --- /dev/null +++ b/mdfinder.hashprovider/IHashProvider.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace mdfinder.hashprovider +{ + public interface IHashProvider + { + /// Gets the name of the hash provider. + /// The name of the hash provider. + string Name { get; } + + /// Gets the description of the hash provider. + /// The description. + string Description { get; } + + /// Gets a value indicating if this hash provider is valid to use. + /// The value indicating if the hash provider is valid. + /// This value can be used by hash providers to limit use of the provider. This can be used to create paid hash providers. + bool IsValid { get; } + + /// Gets or sets the priority this provider has when multiple providers support the same extension. + /// The priority of the hash provider. + int Priority { get; set; } + + /// Gets the file extensions that this hash provider can work with. + /// The file extensions the hash provider works with. + IEnumerable FileExtensions { get; } + + /// Gets a hash from a file. + /// The file to hash. + /// The hash. + string GetHash(FileInfo file); + } +} diff --git a/mdfinder.hashprovider/Properties/AssemblyInfo.cs b/mdfinder.hashprovider/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ffe8708 --- /dev/null +++ b/mdfinder.hashprovider/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("mdfinder.hashprovider")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("mdfinder.hashprovider")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f383e2d1-f62e-44cf-9e58-63542dcbd06e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/mdfinder.hashprovider/mdfinder.hashprovider.csproj b/mdfinder.hashprovider/mdfinder.hashprovider.csproj new file mode 100644 index 0000000..200e96f --- /dev/null +++ b/mdfinder.hashprovider/mdfinder.hashprovider.csproj @@ -0,0 +1,48 @@ + + + + + Debug + AnyCPU + {F383E2D1-F62E-44CF-9E58-63542DCBD06E} + Library + Properties + mdfinder.hashprovider + mdfinder.hashprovider + v4.6.1 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mdfinder.sln b/mdfinder.sln index 4096ad4..7416fc6 100644 --- a/mdfinder.sln +++ b/mdfinder.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdfinder", "mdfinder\mdfinder.csproj", "{E0F831FA-FCE1-471E-8767-D3FD7EDC7CCF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mdfinder.hashprovider", "mdfinder.hashprovider\mdfinder.hashprovider.csproj", "{F383E2D1-F62E-44CF-9E58-63542DCBD06E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {E0F831FA-FCE1-471E-8767-D3FD7EDC7CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0F831FA-FCE1-471E-8767-D3FD7EDC7CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0F831FA-FCE1-471E-8767-D3FD7EDC7CCF}.Release|Any CPU.Build.0 = Release|Any CPU + {F383E2D1-F62E-44CF-9E58-63542DCBD06E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F383E2D1-F62E-44CF-9E58-63542DCBD06E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F383E2D1-F62E-44CF-9E58-63542DCBD06E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F383E2D1-F62E-44CF-9E58-63542DCBD06E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/mdfinder/AboutWindow.xaml b/mdfinder/AboutWindow.xaml new file mode 100644 index 0000000..9ae1466 --- /dev/null +++ b/mdfinder/AboutWindow.xaml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mdfinder/MainWindow.xaml.cs b/mdfinder/MainWindow.xaml.cs index 7599019..9f16c2b 100644 --- a/mdfinder/MainWindow.xaml.cs +++ b/mdfinder/MainWindow.xaml.cs @@ -14,6 +14,10 @@ using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Forms; using System.Threading; +using mdfinder.hashprovider; +using System.Diagnostics; +using System.Reflection; +using System.IO; namespace mdfinder { @@ -24,10 +28,22 @@ namespace mdfinder { #region Properties + /// Gets or sets the database. + /// The database. public DBHelper Database { get; set; } + /// Gets or sets the scanner. + /// The scanner. public Scanner Scanner { get; set; } + /// Gets or sets the hash providers. + /// The hash providers. + public IEnumerable HashProviders { get; set; } + + /// Gets or sets the default provider. + /// The default provider. + public IHashProvider DefaultProvider { get; set; } + #endregion #region Constructors @@ -37,13 +53,22 @@ namespace mdfinder { this.Database = new DBHelper(); this.Scanner = new Scanner(); + this.DefaultProvider = new MD5HashProvider(); + this.HashProviders = GetProviderPlugins(); - this.Scanner.DirectoryFound += (sender, args) => Dispatcher.Invoke(() => txtScanLocation.Text = args.Directory.Name); - this.Scanner.FilesFound += (sender, args) => + this.Scanner.DirectoryFound += (sender, args) => Dispatcher.Invoke(() => txtProgressLabel.Content = args.Directory.Name); + this.Scanner.FilesFound += (sender, args) => { - foreach(var file in args.Files) + foreach (var file in args.Files) { - this.Database.InsertFileRecord(file.FullName, file.Length, Guid.NewGuid().ToString(), "test"); + if (Properties.Settings.Default.SkipEmptyFiles && file.Length == 0) + { + break; + } + + var provider = this.HashProviders.Where(hp => hp.FileExtensions.Contains(file.Extension.ToUpper())).OrderByDescending(hp => hp.Priority).FirstOr(this.DefaultProvider); + this.Database.InsertFileRecord(file.FullName, file.Length, provider.GetHash(file), provider.Name); + Dispatcher.Invoke(() => txtProgressLabel.Content = file.FullName); } }; this.Scanner.ReportProgress += (sender, args) => Dispatcher.Invoke(() => { if (args.Processed > 0) { this.progressBar.Value = args.Percentage * 100; } }); @@ -55,16 +80,40 @@ namespace mdfinder #region Methods + /// Gets the provider plugins from a folder. + /// + /// An enumerator that allows foreach to be used to process the provider plugins in this + /// collection. + /// + private IEnumerable GetProviderPlugins() + { + var directory = new DirectoryInfo(Properties.Settings.Default.ProviderFolder); + foreach (var pluginFile in directory.GetFiles("*.dll")) + { + var assembly = Assembly.LoadFrom(pluginFile.FullName); + foreach (var type in assembly.GetTypes().Where(t => t.GetInterface("IHashProvider") != null)) + { + yield return Activator.CreateInstance(type) as IHashProvider; + } + } + } + + /// Event handler. Called by btnFilePicker for click events. + /// Source of the event. + /// Routed event information. private void btnFilePicker_Click(object sender, RoutedEventArgs e) { var fbd = new FolderBrowserDialog(); - if(fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK) + if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { txtScanLocation.Text = fbd.SelectedPath; } } + /// Event handler. Called by btnScan for click events. + /// Source of the event. + /// Routed event information. private void btnScan_Click(object sender, RoutedEventArgs e) { var location = txtScanLocation.Text; @@ -73,10 +122,75 @@ namespace mdfinder new Thread(() => { this.Scanner.Scan(location); + this.Dispatcher.Invoke(() => txtProgressLabel.Content = string.Empty); + this.Dispatcher.Invoke(() => datagridFileRecords.ItemsSource = this.Database.GetFileRecords()); }).Start(); } } + /// Event handler. Called by DatagridFileRecords for initialized events. + /// Source of the event. + /// Event information. + private void DatagridFileRecords_Initialized(object sender, EventArgs e) + { + this.datagridFileRecords.ItemsSource = this.Database.GetFileRecords(); + } + + /// Event handler. Called by BtnFilterDuplicates for click events. + /// Source of the event. + /// Routed event information. + private void BtnFilterDuplicates_Click(object sender, RoutedEventArgs e) + { + this.datagridFileRecords.ItemsSource = new ListCollectionView(this.Database.GetFileRecords().GroupBy(fr => fr.Hash).Where(g => g.Count() > 1).SelectMany(g => g).ToList()); + ((ListCollectionView)this.datagridFileRecords.ItemsSource).GroupDescriptions.Add(new PropertyGroupDescription("Hash")); + } + + /// Event handler. Called by BtnFilterShowAll for click events. + /// Source of the event. + /// Routed event information. + private void BtnFilterShowAll_Click(object sender, RoutedEventArgs e) + { + this.datagridFileRecords.ItemsSource = this.Database.GetFileRecords(); + } + + /// Event handler. Called by BtnClear for click events. + /// Source of the event. + /// Routed event information. + private void BtnClear_Click(object sender, RoutedEventArgs e) + { + this.Database.Clear(); + this.datagridFileRecords.ItemsSource = Enumerable.Empty(); + } + + /// Event handler. Called by Hyperlink for click events. + /// Source of the event. + /// Routed event information. + private void Hyperlink_Click(object sender, RoutedEventArgs e) + { + Hyperlink link = (Hyperlink)e.OriginalSource; + Process.Start(link.NavigateUri.AbsoluteUri); + } + + /// Event handler. Called by MenuOptions for click events. + /// Source of the event. + /// Routed event information. + private void MenuOptions_Click(object sender, RoutedEventArgs e) + { + var optionsWindow = new OptionsWindow(); + + optionsWindow.ShowDialog(); + + this.HashProviders = GetProviderPlugins(); + } + + private void MenuAbout_Click(object sender, RoutedEventArgs e) + { + var aboutWindow = new AboutWindow(); + + aboutWindow.Show(); + } #endregion + + } } diff --git a/mdfinder/OptionsWindow.xaml b/mdfinder/OptionsWindow.xaml new file mode 100644 index 0000000..fb16a2b --- /dev/null +++ b/mdfinder/OptionsWindow.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + +