Click here to Skip to main content
15,891,375 members
Articles / Programming Languages / C#

Multithreaded File/Folder Finder

Rate me:
Please Sign up or sign in to vote.
4.19/5 (18 votes)
26 May 2010CPOL17 min read 110.5K   5.6K   125  
File Find is fast, especially if you have multiple physical drives; version 2.1.0.17.
#define useIsolatedStorage
//The XML configuration file may be written to isolated storage or to normal file storage.
//Normal file storage is useful for debugging as it is easy to view or modify the XML source.

using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.IO.IsolatedStorage;
using System.Security;
using System.Text;
using System.Windows.Forms;
using System.Xml;

namespace FileFind
{
    /// <summary>
    ///   ConfigInfo holds configuration data as well as data that is commonly shared between 
    ///   program modules and forms.
    ///   Product/version information is maintained in AssemblyInfo.cs
    ///   Configuration information is stored when the application exits and retrieved at the next start up.
    ///   If useIsolatedStorage is #defined, application setting are saved in isolated storage specific to 
    ///  the program and user. Otherwise, the information is stored in the program execution directory
    ///  where it is simple to view or modify the XML source for testing.
    /// 
    ///   ConfigInfo is also used to pass data from the DiskSelection dialog back to the caller.
    /// </summary>
    class ConfigInfo
    {
        #region Local constants and variables
        private object syncobj = new object();      //local lock object

        /// <summary>
        /// Product/version identification constants
        /// </summary>
        private const string PRODUCTNAME = "File Find";
        private const string PRODUCTDATE = "2008";
        private static string PRODUCTVERSION = 
            System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
        /// <summary>
        /// Maintenance
        /// Version 1.0.0.0 Initial coding
        ///         2.0.0.0 Code restructured and optimized based on hard won knowledge
        ///         2.1.0.0 Added ResolvePartitions logic to Form1.cs to eliminate
        ///             seek contention on partitioned physical drives.
        ///         2.1.0.3 Added reparse point processing (do not recurse into it)
        ///         2.1.0.4 Added time filters
        ///         2.1.0.5 Added hidden folder/file menu option
        ///         2.1.0.6 Switched from foreach (FileSystemInfo to FileSystemEnumerator in DiskScan
        ///             to significantly speed up access to network drives
        ///         2.1.0.7 Added Menu->Options->Scanning via enumerator to ease testing and
        ///             allow for Windows 7 bug. Windows 7 failed when using enumerator.
        ///         2.1.0.8 Added NumericScaling class
        ///         2.1.0.9 Final clean up before publication to www.codeproject.com
        ///         2.1.0.10 Changed .cvs to .csv file extention
        ///             Added code to correctly handle embedded commas, new line characters and double quotes for save to .CSV files
        ///             Modified exit from Excludes to refresh selected drive information
        ///             Modified .CSV save routine to properly handle reservered characters (comma and double quotes) and changed
        ///             the seperation character to a tab.
        ///         2.1.0.11 Modified menu titles and rearranged menu items. Changed opening instructions.
        ///             Added status area at bottom of form to report disks being searched and thread terminations.
        ///             Modified SelectAllDisks logic to handle a system with no drives.
        ///         2.1.0.12
        ///             Added "Waiting on logical/physical drive resolution" message to Form1.cs
        ///             Grid view of "Folders only" no longer has file name or size columns
        ///         2.1.0.13
        ///             Added busy cursor during logical/physical disk resolution, Sort and Grid view swap.
        ///             Totally revamped BeginUpdate/EndUpdate logic for filesFoundBox displays resulting in
        ///             better response and faster searches when a large number of hits are found.
        ///         2.1.0.14
        ///             Moved logical to physical disk resolution to LogicalPhysicalDiskResolution class
        ///             Corrected Form1.excludesButton_Click to resolve with currently selected disk drives.
        ///             Add disk info display to single/double click of Disk column in grid view.
        ///             Now launches Drive for double click.
        ///         2.1.0.15
        ///             Improved user identity checking in About.
        ///             Corrected an error in Directory delete
        /// </summary>
        private const string CONFIGFILENAME = "FileFind.xml"; //name of the configuration file

        private static bool alreadyInitialized = false; //determines if the following static fields are already loaded
        private static bool isDirty = false;            //determines if ConfigInfo should be saved at exit
        private static bool removedSavedOptions = false;    //Isolated storage is not to be written at termination

        public enum DateTimeRangeSearch
        {
            DateTimeRangeOff,
            DateTimeRangeByCreate,
            DateTimeRangeByModified,
            DateTimeRangeByLastAccessed
        };
        private static DateTimeRangeSearch dateTimeRange;
        private static DateTime fromDateTime;
        private static DateTime toDateTime;

        //variables held for Form to Form communications
        private static ArrayList selectedDisks;         //contains: string of disk letters
                                                        //for example: C:\, D:\, etc

        //variables to be saved for the next execution of this program
        private static ArrayList ignoreFolders = null;  //contains files and folders to exclude when searching
                                                        //contains: string
        private static bool startWithGrid = false;      //start in List or Grid view
        private static Font font = null;                //display panels in this font
        private static bool showUnauthorizedFolders;    //if True, security exceptions are displayed
        private static bool showHiddenFoldersFiles;     //if True, search and show hidden folders and files
        private static bool scanningViaEnumerator;      //if True, search files and folders using enumerators
        private static bool autoScrolling;              //if true, auto scroll list view

        #endregion Local constants and variables

        #region Properties
        public string ProductName
        { get { return PRODUCTNAME; } }

        public string ProductVersion
        { get { return PRODUCTVERSION; } }

        public string ProductDate
        { get { return PRODUCTDATE; } }

        public bool StartWithGrid
        {   //determines if the initial display should begin with a list or grid view
            //List view is the inital default
            //The current view is saved when the application exits and the
            //application will be restarted with the same view.
            get { return startWithGrid; }
            set
            {
                if(value != startWithGrid)
                { startWithGrid = value; isDirty = true; }
            }
        }

        public bool ShowUnauthorizedFolders
        {   //determines if a display of failed folder accesses should be created or not
            get { return showUnauthorizedFolders; }
            set { showUnauthorizedFolders = value; }
        }

        public bool ShowHiddenFoldersFiles
        {   //determines if hidden folders or files should be shown and searched
            get { return showHiddenFoldersFiles; }
            set { showHiddenFoldersFiles = value; isDirty = true; }
        }

        public bool ScanningViaEnumerator
        {
            get { return scanningViaEnumerator; }
            set { scanningViaEnumerator = value; isDirty = true; }
        }

        public bool AutoScrolling
        {
            get { return autoScrolling; }
            set { autoScrolling = value; isDirty = true; }
        }

        public ArrayList IgnoreFolders
        {   //a set of folder nodes, drives or specific drive folder combinations to be excluded
            //populated by the IgnoreFolders class
            get { lock (syncobj) { return ignoreFolders; } }
            set { lock (syncobj) { ignoreFolders = value; isDirty = true; } }
        }

        public ArrayList SelectedDisks
        {   //set of drives selected to be searched
            //populated by the DiskSelection class
            get
            {
                lock (syncobj)
                {
                    ArrayList selected = new ArrayList(selectedDisks);
                    return selected;
                }
            }
            set { lock (syncobj) { selectedDisks = value; } }
        }

        public Font UseFont
        {   //font to use for displays
            //selected via menu option
            get { lock (syncobj) { return font; } }
            set { lock (syncobj) { font = value; isDirty = true; } }
        }

        public ArrayList DefaultFolders
        {   //generate a default exclusion list
            get
            {
                ArrayList defaultFolders = new ArrayList();
                defaultFolders.Add("System Volume Information");
                defaultFolders.Add("$Recycle.Bin");
                defaultFolders.Add("RECYCLER");             //NTFS
                defaultFolders.Add("RECYCLED");             //FAT
                defaultFolders.Add("Program Files");        //found on 32 and 64 bit systems
                defaultFolders.Add("Program Files (x86)");  //found on 64 bit systems
                return defaultFolders;
            }
        }

        public DateTimeRangeSearch IDateTimeRange
        {
            get { return dateTimeRange; }
            set { dateTimeRange = value; }
        }

        public DateTime IFromDateTime
        {
            get { return fromDateTime; }
            set { fromDateTime = value; }
        }

        public DateTime IToDateTime
        {
            get { return toDateTime; }
            set { toDateTime = value; }
        }

        #endregion Properties

        #region Public functions
        /// <summary>
        /// Class constructor
        /// On first use, initializes all the static fields to default values then loads
        /// stored values from a prior execution.
        /// </summary>
        public ConfigInfo()
        {
            if (!alreadyInitialized)
            {   //initialize all configuration data when first called
                lock (syncobj)
                {
                    alreadyInitialized = true;
                    selectedDisks = new ArrayList();
                    ignoreFolders = new ArrayList();
                    dateTimeRange = DateTimeRangeSearch.DateTimeRangeOff;
                    DateTime currentDateTime = DateTime.Now;
                    //fromDateTime = DateTime.MinValue;
                    fromDateTime = new DateTime(1980, 1, 1, 0, 0, 0);
                    toDateTime = DateTime.MaxValue;
                    /*toDateTime = new DateTime(currentDateTime.Year,
                        currentDateTime.Month,
                        currentDateTime.Day,
                        currentDateTime.Hour, 23, 59); */
                    scanningViaEnumerator =
                    showHiddenFoldersFiles =
                    showUnauthorizedFolders =
                    startWithGrid = false;
                    autoScrolling = true;
                    try
                    {
#if useIsolatedStorage
                        using (Stream isolatedStorageStream = new IsolatedStorageFileStream(CONFIGFILENAME,
                            FileMode.Open, IsolatedStorageFile.GetUserStoreForAssembly()))
#else
                        using (Stream isolatedStorageStream = new FileStream(CONFIGFILENAME, FileMode.Open))
#endif
                        {
                            XmlDocument xmldoc = new XmlDocument();
                            using (StreamReader ifs = new StreamReader(isolatedStorageStream))
                            { xmldoc.Load(ifs); }
                            XmlNode listOrGrid = xmldoc.SelectSingleNode("FileFind/StartView");
                            if ((null != listOrGrid) && ("Grid" == listOrGrid.InnerText))
                                startWithGrid = true;

                            XmlNode showHidden = xmldoc.SelectSingleNode("FileFind/Hidden");
                            if ((null != showHidden) && ("True" == showHidden.InnerText))
                                showHiddenFoldersFiles = true;

                            XmlNode scanning = xmldoc.SelectSingleNode("FileFind/ScanningViaEnumerator");
                            if ((null != scanning) && ("True" == scanning.InnerText))
                                scanningViaEnumerator = true;

                            XmlNode autoScroll = xmldoc.SelectSingleNode("FileFind/AutoScroll");
                            if (null != autoScroll)
                                if ("True" == autoScroll.InnerText)
                                    autoScrolling = true;
                                else
                                    autoScrolling = false;

                            XmlNode ignore = xmldoc.SelectSingleNode("FileFind/IgnoreFolders");
                            if (null != ignore)
                                foreach (XmlNode x in ignore)
                                    ignoreFolders.Add(x.InnerText);
                            else
                                ignoreFolders = DefaultFolders;

                            try
                            {
                                XmlNode useFont = xmldoc.SelectSingleNode("FileFind/UseFont");
                                if (null != useFont)
                                {
                                    XmlNode root;
                                    root = xmldoc.SelectSingleNode("FileFind/UseFont/Name");
                                    string fontName = root.InnerText;
                                    root = xmldoc.SelectSingleNode("FileFind/UseFont/Size");
                                    string swFont = root.InnerText;
                                    float currentSize = System.Convert.ToSingle(swFont);
                                    FontStyle swStyle = new FontStyle();
                                    root = xmldoc.SelectSingleNode("FileFind/UseFont/Bold");
                                    if ("True" == root.InnerText)
                                        swStyle |= FontStyle.Bold;
                                    root = xmldoc.SelectSingleNode("FileFind/UseFont/Italic");
                                    if ("True" == root.InnerText)
                                        swStyle |= FontStyle.Italic;
                                    root = xmldoc.SelectSingleNode("FileFind/UseFont/Strikeout");
                                    if ("True" == root.InnerText)
                                        swStyle |= FontStyle.Strikeout;
                                    root = xmldoc.SelectSingleNode("FileFind/UseFont/Underline");
                                    if ("True" == root.InnerText)
                                        swStyle |= FontStyle.Underline;
                                    font = new Font(fontName, currentSize, swStyle);
                                }
                            }
                            catch (Exception xe)
                            {
                                MessageBox.Show(xe.InnerException.ToString(), PRODUCTNAME + " UseFont",
                                  MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            }
                        }
                    }
                    catch (SecurityException se)
                    {
                        MessageBox.Show(se.Message, PRODUCTNAME + " Config loading",
                               MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                    }
                    catch (Exception)
                    { }
                } //ends lock (syncobj)
            } //ends if (!alreadyInitialized)
        } //ends public ConfigInfo()

        /// <summary>
        /// Saves configuration values for the next execution of the application
        /// </summary>
        public void SaveConfig()
        {   //XML Create (creates an XML file via native calls)
            if((isDirty) && (!removedSavedOptions))
            lock (syncobj)
            {
                try
                {
#if useIsolatedStorage
                    using (Stream isolatedStorageStream =
                        new IsolatedStorageFileStream(CONFIGFILENAME, FileMode.Create, IsolatedStorageFile.GetUserStoreForAssembly()))
#else
                    using (Stream isolatedStorageStream = new FileStream(CONFIGFILENAME, FileMode.Create))
#endif
                    using(XmlTextWriter appconfig = new XmlTextWriter(isolatedStorageStream, Encoding.UTF8))
                    {
                        appconfig.Formatting = Formatting.Indented;
                        appconfig.WriteStartDocument();
                        DateTime newDate = DateTime.Now;
                        appconfig.WriteComment("Created " + newDate);
                        appconfig.WriteStartElement("FileFind");
                        appconfig.WriteCData(@"<>\&");

                        if (startWithGrid)
                            appconfig.WriteElementString("StartView", "Grid");
                        else
                            appconfig.WriteElementString("StartView", "List");

                        appconfig.WriteElementString("Hidden", showHiddenFoldersFiles.ToString());
                        appconfig.WriteElementString("ScanningViaEnumerator", 
                            scanningViaEnumerator.ToString());
                        appconfig.WriteElementString("AutoScroll",
                            autoScrolling.ToString());

                        if (null != ignoreFolders)
                        {
                            appconfig.WriteStartElement("IgnoreFolders");
                            foreach (string str in ignoreFolders)
                                appconfig.WriteElementString("IgnoreFolder", str);
                            appconfig.WriteEndElement();    //ends IgnoredFolders
                        }

                        if (null != font)
                        {
                            appconfig.WriteStartElement("UseFont");
                            appconfig.WriteElementString("Name", font.Name.ToString());
                            appconfig.WriteElementString("Style", font.Style.ToString());
                            appconfig.WriteElementString("Size", font.Size.ToString());
                            appconfig.WriteElementString("Bold", font.Bold.ToString());
                            appconfig.WriteElementString("Italic", font.Italic.ToString());
                            appconfig.WriteElementString("Strikeout", font.Strikeout.ToString());
                            appconfig.WriteElementString("Underline", font.Underline.ToString());
                            appconfig.WriteEndElement();    //ends UseFont
                        }

                        appconfig.WriteEndDocument();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, PRODUCTNAME,
                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            } //ends lock (syncobj)
        } //ends public void SaveConfig()

        public ArrayList SelectAllDisks(out int diskCount)
        {   //show all disks as selected except explicitly excluded disks
            //returns an ArrayList of selected disks and a count of selected disks
            //if the count is -1 then all disks have been selected
            ArrayList selected = new ArrayList();
            DriveInfo[] allDrives = null;
            try
            { allDrives = DriveInfo.GetDrives(); }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, PRODUCTNAME + " SelectAllDisks",
                                MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
                allDrives = null;
            }
            diskCount = 0;
            if (allDrives != null)
            {
                int readyDrives = 0;
                lock (syncobj)
                {   //freeze contents of ignoreFolders
                    foreach (DriveInfo drive in allDrives)
                    {//show drives available
                        if (drive.IsReady)
                        {
                            ++readyDrives;
                            if (!FoundInIgnoreDirectory(drive.Name))
                                selected.Add(drive.Name);
                        }
                    }
                } //ends lock (syncobj)
                diskCount = selected.Count;
                if (diskCount == readyDrives)
                    diskCount = -1; //all drives selected
            }
            return selected;
        } //ends public ArrayList SelectAllDisks(...

        public ArrayList FilterSelectedDisks(ArrayList currentlySelected)
        {   //show all disks as selected except explicitly excluded disks
            //returns an ArrayList of selected disks and a count of selected disks
            //if the count is -1 then all disks have been selected
            ArrayList selected = new ArrayList();
                lock (syncobj)
                {   //freeze contents of ignoreFolders
                    foreach (string selectedDrive in currentlySelected)
                    {//show drives available
                        DriveInfo drive = new DriveInfo(selectedDrive);
                        if (drive.IsReady)
                        {
                            if (!FoundInIgnoreDirectory(drive.Name))
                                selected.Add(drive.Name);
                        }
                    }
                } //ends lock (syncobj)
            return selected;
        } //ends public ArrayList FilterSelectedDisks(...

        public void RemoveIsolatedStorage()
        {
            removedSavedOptions = true;
            try
            {
#if useIsolatedStorage
                IsolatedStorageFile isoFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
                    IsolatedStorageScope.Assembly,
                    typeof(System.Security.Policy.Url),
                    typeof(System.Security.Policy.Url));

                String[] fileNames = isoFile.GetFileNames(CONFIGFILENAME);

                foreach (string fileName in fileNames)
                {
                    // Delete the files.
                    isoFile.DeleteFile(fileName);
                }
#else
                    File.Delete(CONFIGFILENAME);
#endif
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, PRODUCTNAME + " Isolated storage / file delete",
                                     MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        } //ends public RemoveIsolatedStorage()
        #endregion Public functions

        #region Private functions
        private bool FoundInIgnoreDirectory(string driveLetter)
        {   //Determines if a disk has been excluded.
            //This routine does not have hidden file logic.
            if (String.IsNullOrEmpty(driveLetter) || (driveLetter.Length < 3))
                return true;
            string fwdSlash = driveLetter.Replace('\\', '/');
            foreach (string str in ignoreFolders)
            {
                if (':' == str[1])
                {   //fully qualified folder name by drive?
                    if (Char.IsLetter(str[0]))
                    {
                        string folder_str = str.Replace('\\', '/');
                        if (string.Compare(folder_str, fwdSlash, true) == 0)
                            return true;
                    }
                }
                if (string.Compare(str, fwdSlash, true) == 0)
                    return true;
            }
            return false;
        } //ends private bool FoundInIgnoreDirectory(...
        #endregion Private functions
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) retired
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions