Click here to Skip to main content
15,892,809 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.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Microsoft.VisualBasic.FileIO;

/// <summary>
/// File Find searches for file and/or folder names matching one or more masks.
/// A mask may contain wild card characters (* or ?). An asterick will match one or more characters and the question
/// mark will match any single character. All matching is case insensitive.
/// </summary>
/// 

//To do:

//
//Added Invisible form if arguments present
//Added Export/import FilterListCollection
//Added a "take a breath" logic to DiskScan
//Accepts argument for locations and masks to search
//Allowed include / exclude location lists
//Add a "take a breath" logic to DiskScan
//Added Volume name to grid
//Added columns to view to grid
//Problem: last row remained visible when all rows were selected for delete
//Fixed: Form1 error when exiting under Windows 7
//Created folder & file column save/restore

namespace FileFind
{
    public partial class Form1 : Form
    {
        //--------------------------------------------------------------------------------------------------
        //grid view table, constants and variables
        //table columns, update BuildGridColumnSelectionMenu when adding to or deleting from these constants
        private const string GTFILENAME = "File name";
        private const string GTSIZE = "Size";
        private const string GTDISK = "Disk";
        private const string GTVOLUMEID = "Volume Id";
        private const string GTDIRECTORY = "Directory";
        private const string GTCREATED = "Created";
        private const string GTMODIFIED = "Modified";
        private const string GTLASTACCESSED = "Last accessed";
        //holds local copy of grid view column ordering & visibility information
        private DataTable table;    //constructed in BuildGridTable()
        GridInfoCollection
            foldersView = null,     //folders only display
            filesView = null,       //files or files and folders display
            usingView = null;       //which view (files or folders) is currently active in the grid
        //used to remember which cell was clicked in a prior grid event
        private int cellRowClicked = -1;
        private string cellColumnName = null;

        //--------------------------------------------------------------------------------------------------
        //various button titles and message texts
        private const string STARTSEARCH = "Start search";
        private const string SEARCHSTOPPING = "Search stopping";
        private const string STOPSEARCH = "Stop search";
        private const string ITEMSFOUND = "Items found: ";
        private const string MENUGRIDVIEW = "Grid view";
        private const string MENULISTVIEW = "List view";
        private const string SEARCHALLDISKS = "All disks";
        private const string SEARCHSELECTEDDISKS = "Selected disks";
        private const string SEARCHNONE = "None";
        private const string DELETE = "Delete";             //used with sortOutputButton
        private const string SORTOUTPUT = "Sort output";    //used with sortOutputButton
        private const string SELECTTIMERANGE = "Select time range";
        private const string SELECTEDTIMERANGE = "Selected times";
        private const string SCANNINGVIAENUMERATOR = "Scanning via enumerator";


        //--------------------------------------------------------------------------------------------------
        private DateTimeInfo dateTimeInfo;  //holds return info from SelectDateTimeRange

        //--------------------------------------------------------------------------------------------------
        // Variables dealing with disk drive resolution
        // Logical to physical drive resolution is drived asynchronously by FetchAllDisks
        // or FetchSelectedDisks. The async delegate was used to eliminate
        // a brief pause caused by the WMI calls just before DiskScan threads were
        // attached. ResolveLogicalPhysicalDisks.BeginResolveDrives allows the WMI calls
        // to run as soon as selectedDisks is loaded. The thread attach logic 
        // waits for ResolveLogicalPhysicalDisks.EndResolveDrives to insure disk partitions have 
        // been resolved before the threads are attached. The resolvedInUse variable insures only one disk
        // resolution at time is running so the global level selectedDisks and resolvedDrives
        // variables will not be corrupted.
        ResolveLogicalPhysicalDisks resolveLogicalPhysicalDisks = new ResolveLogicalPhysicalDisks();
        private volatile ManualResetEvent resolvedInUse = new ManualResetEvent(false);
        private volatile int resolutionsScheduled = 0;
        private ArrayList selectedDisks;   //disks to search
            //contains: string C:\, D:\, etc
            //one letter designation per entry
            //selectedDisks is initially set during form load byt the HandleDriveSelection() routine
            //and will also be set by the disksToSearchButton_Click routine.
        private ArrayList resolvedDrives;
            //contains: string C;\, D:\;E:\, G:\ etc
            //Each physical drive is represented by the letter designations of the
            //partitions on the drive. Partitions on the same physical drive are
            //within the same comma group.
            //resolvedDrives is set in the ResolutionCompleted routine.

        //--------------------------------------------------------------------------------------------------
        // delegates used to call display functions from worker threads
        private delegate void DelegateSendMsg(String s);
        private DelegateSendMsg m_DelegateSendMsg;
        private delegate void DelegateThreadStopped(string s);
        private DelegateThreadStopped m_DelegateThreadStopped;
        private delegate int SendFileInfo(FileInfo fi, string volumeId);
        private SendFileInfo m_SendFileInfo;
        private delegate int SendDirectoryInfo(DirectoryInfo dir, string volumeId);
        private SendDirectoryInfo m_SendDirectoryInfo;

        private delegate void DelegateUpdateStatus(String s);
        private DelegateUpdateStatus m_DelegateUpdateStatus;

        //--------------------------------------------------------------------------------------------------
        // variables used for multithreading control
        private object threadobj = new object();                        //Thread control lock object
        private List<Thread> searchThreads = new List<Thread>();        //Active search threads

        private ManualResetEvent pleaseTerminate = new ManualResetEvent(false);    //Set to true if the thread is to terminate early
        private ManualResetEvent allThreadsTerminated = null;   //used only when Form1 is closing
        private delegate void TerminateThreads();

        //--------------------------------------------------------------------------------------------------
        private object syncobj = new object();      //local lock object for form displays
        private int                                 //statistics (update only when protected by lock(syncobj))
            filesFound = 0,
            directoriesFound = 0,
            itemsFound = 0;

        //fields to control BeginUpdate/EndUpdate for filesFoundBox
        private DateTime
            priorTime,
            thisTime;
        private int msgsPerInterval = 0;
        private const int MaxRate = 300;        //maximum number of messages per MsgInterval
        private const int PauseValue = 20;      //milliseconds to pause busy threads
        private const long MsgInterval = 500;   //milliseconds between message intervals
        private enum InUpdate { InUpdateNo, InUpdateSpeed, InUpdatePausing }
        private InUpdate inUpdate = InUpdate.InUpdateNo;

        private bool autoScrollMode = true;         //list view scroll mode
        private bool foldersOnlyDisplay;            //type of grid that is in use
        private bool performSequentialSearch = false;   //sequentially search all disks

        //--------------------------------------------------------------------------------------------------
        Stopwatch elaspedTime = new Stopwatch();    //used to measure elasped search time

        //batch interface fields
        private NamedPipeClientStream pipe = null;  //pipe is built if arguments were passed

        public Form1()
        { InitializeComponent(); }

        /// <summary>
        /// Update the tool strip status line
        /// </summary>
        /// <param name="statusLine">single line message</param>
        private void UpdateStatus(string statusLine)
        {
            try
            {
                if (this.InvokeRequired)
                    this.BeginInvoke(m_DelegateUpdateStatus, new Object[] { statusLine });
                else
                    lock (syncobj)
                        toolStripStatusLabel1.Text = statusLine;
            }
            catch { }
        } //ends private void UpdateStatus(...

        /// <summary>
        /// Writes one line to the list display. Will enter/exit filesFoundBox update mode
        /// depending on message traffic per interval.
        /// <para>High rates of message delivery caused unacceptable response times when attempting to stop  
        /// a scan or close the application.
        /// </para>
        /// <para>If more than MaxRate messages are delivered within MsgInterval milliseconds, then 
        /// MsgDisplayNolock will stop autoscroll and enter filesFoundBox.BeginUpdate() mode and start a timer to issue
        /// a filesFoundBox.EndUpdate() call. 
        /// </para>
        /// <para>When filesFoundBox enters the BeginUpdate() mode, the grid view is placed in
        /// a BeginLoadData() mode. A pause interval is then calculated for timer1 based on the numer
        /// of active threads plus 10 milliseconds. 'timer1' is started.
        /// </para>
        /// If the MaxRate * 2 is exceeded while in Begin/End update mode, pauses are issued to the 
        /// DiskScan routines.
        /// <para>The timer1_Tick routine issues the EndUpdate() and EndLoadData() commands and restores autoscroll if necessary.
        /// </para>
        /// <para>A visual indicator of high message volume is given by continuing to display
        /// the ITEMSFOUND message but not updating the form when the number of messages exceeds msgCountMax.
        /// </para>
        /// <para>All updates to the message traffic variables must be made while lock(syncobj) is in effect.
        /// </para>
        /// </summary>
        /// <param name="msg">line to be written</param>
        private int MsgDisplayNolock(String msg)
        {   //Display a single line message without issuing a local lock(syncobj).
            //This routine expects the caller to own the lock.
            int pause = 0;  //milliseconds disk scan should pause
            listBox1.Items.Add(msg);
            if (listBox1.Items.Count > MaxRate)   //check pacing only when reporting a large number of items
                switch (inUpdate)
                {
                    case InUpdate.InUpdateNo:
                        if (autoScrollMode)
                            listBox1.TopIndex = listBox1.Items.Count - 1;
                        if (++msgsPerInterval > MaxRate)    //check message traffic rate
                        {   //how long did it take to reach maximum number of messages per interval?
                            thisTime = DateTime.Now;
                            if ((thisTime.Millisecond - priorTime.Millisecond) <= MsgInterval)
                            {   //enter BeginUpdate mode
                                listBox1.BeginUpdate();
                                table.BeginLoadData();
                                inUpdate = InUpdate.InUpdateSpeed;
                                timer1.Interval = 10 + PauseValue * 2;
                                timer1.Enabled = true;
                            }
                            priorTime = thisTime;
                            fileCountLabel.Text = ITEMSFOUND + itemsFound.ToString("N0", CultureInfo.CurrentCulture);
                            msgsPerInterval = 0;
                        }
                        break;
                    case InUpdate.InUpdateSpeed:
                        if (++msgsPerInterval > MaxRate * 2)
                        {
                            inUpdate = InUpdate.InUpdatePausing;    //enter pause mode
                            listBox1.EndUpdate();
                            table.EndLoadData();
                        }
                        break;
                    case InUpdate.InUpdatePausing:
                        fileCountLabel.Text = ITEMSFOUND + itemsFound.ToString("N0", CultureInfo.CurrentCulture);
                        pause = PauseValue;
                        break;
                } //ends if (filesFoundBox.Items.Count > MaxRate)
            else
            {
                fileCountLabel.Text = ITEMSFOUND + itemsFound.ToString("N0", CultureInfo.CurrentCulture);
                if (autoScrollMode)
                    listBox1.TopIndex = listBox1.Items.Count - 1;
            }
            return pause;
        } //ends private void MsgDisplayNolock

        /// <summary>
        /// Activated only if an "excessive" number of messages are being processed per time interval
        /// </summary>
        private void timer1_Tick(object sender, EventArgs e)
        {
            lock (syncobj)
            {   //protect against concurrent updates of inUpdate and timers
                switch (inUpdate)
                {
                    case InUpdate.InUpdateNo:
                        break;
                    case InUpdate.InUpdateSpeed:
                        //exit BeginUpdate mode
                        listBox1.EndUpdate();
                        table.EndLoadData();
                        if (msgsPerInterval > MaxRate * 2)
                            inUpdate = InUpdate.InUpdatePausing;    //enter pause mode
                        else if (msgsPerInterval < MaxRate)
                        {
                            timer1.Enabled = false;
                            inUpdate = InUpdate.InUpdateNo;
                            msgsPerInterval = 0;
                            priorTime = DateTime.Now;
                        }
                        if (autoScrollMode)
                            listBox1.TopIndex = listBox1.Items.Count - 1;
                        break;
                    case InUpdate.InUpdatePausing:
                        timer1.Enabled = false;
                        inUpdate = InUpdate.InUpdateNo;
                        msgsPerInterval = 0;
                        priorTime = DateTime.Now;
                        if (autoScrollMode)
                            listBox1.TopIndex = listBox1.Items.Count - 1;
                        break;
                }
            } //ends lock (syncobj)
        } //ends private void timer1_Tick(...

        /// <summary>
        /// Send a single or multiple line message to the list view
        /// </summary>
        /// <param name="textLines">message(s) to be displayed</param>
        private void SendMsg(string textLines)
        {   //display multi-line messages
            //may be driven by search threads
            lock (syncobj)
            {
                while (textLines != null)
                {
                    int ndx = textLines.IndexOf(Environment.NewLine);
                    if (ndx > -1)
                    {
                        MsgDisplayNolock(textLines.Substring(0, ndx));
                        if (ndx < textLines.Length)
                        {
                            textLines = textLines.Substring(ndx + 1);
                            if (Environment.NewLine == textLines.Substring(0, 1))
                                textLines = textLines.Substring(1);
                        }
                        else
                            textLines = null;
                    }
                    else
                    {
                        MsgDisplayNolock(textLines);
                        textLines = null;
                    }
                } //ends while (textLines != null)
            } //ends lock (syncobj)
        } //ends private void SendMsg(...

        /// <summary>
        /// FileFind was activated with parameters. Output is being sent
        /// to a pipe provided by the activator.
        /// </summary>
        /// <param name="msg">information to be displayed</param>
        private void SendToPipe(string msg)
        {
            byte[] bytes = Encoding.Default.GetBytes(msg + "\n");
            try { pipe.Write(bytes, 0, bytes.Length); }
            catch { pleaseTerminate.Set(); }
        }

        /// <summary>
        /// Called by DiskScan threads when a file matches search criteria
        /// </summary>
        /// <param name="fi">file to report on</param>
        /// <param name="volumeId">volume file was found on</param>
        /// <returns>pause interval if traffic is excessive</returns>
        private int ReceiveFileInfo(FileInfo fi, string volumeId)
        {   //add file information to the grid view
            //Invoked by search threads
            int pause = 0;  //milliseconds disk scan should pause
            lock (syncobj)
            {
                if (null == pipe)
                {
                    string dir = fi.DirectoryName;
                    string disk = dir.Substring(0, dir.IndexOf(@"\") + 1);
                    dir = dir.Substring(disk.Length);
                    ++filesFound;
                    ++itemsFound;
                    pause = MsgDisplayNolock(fi.FullName);  //update the list view
                    DataRow newRow = table.NewRow();
                    newRow[GTFILENAME] = fi.Name;
                    newRow[GTSIZE] = fi.Length;
                    newRow[GTDISK] = disk;
                    newRow[GTVOLUMEID] = volumeId;
                    newRow[GTDIRECTORY] = dir;
                    newRow[GTCREATED] = fi.CreationTime;
                    newRow[GTMODIFIED] = fi.LastWriteTime;
                    newRow[GTLASTACCESSED] = fi.LastAccessTime;
                    table.Rows.Add(newRow);                 //update the grid view
                }
                else
                    SendToPipe(fi.FullName);
            } //ends lock(syncobj)
            return pause;
        } //ends private void ReceiveFileInfo(...

        /// <summary>
        /// Called by DiskScan threads when a folder matches search criteria
        /// </summary>
        /// <param name="dir">directory to report on</param>
        /// <param name="volumeId">volume directory was found on</param>
        /// <returns>pause interval if traffic is excessive</returns>
        private int ReceiveDirInfo(DirectoryInfo dir, string volumeId)
        {   //add directory information to the grid view
            //Invoked by search threads
            int pause = 0;  //milliseconds disk scan should pause
            lock (syncobj)
            {
                if (null == pipe)
                {
                    string dirname = dir.FullName;
                    string disk = dirname.Substring(0, dirname.IndexOf(@"\") + 1);
                    dirname = dirname.Substring(disk.Length);
                    ++directoriesFound;
                    ++itemsFound;
                    pause = MsgDisplayNolock(dir.FullName); //update the list view
                    DataRow newRow = table.NewRow();
                    newRow[GTDISK] = disk;
                    newRow[GTVOLUMEID] = volumeId;
                    newRow[GTDIRECTORY] = dirname;
                    newRow[GTCREATED] = dir.CreationTime.ToString();
                    newRow[GTMODIFIED] = dir.LastWriteTime.ToString();
                    newRow[GTLASTACCESSED] = dir.LastAccessTime.ToString();
                    table.Rows.Add(newRow);                 //update the grid view
                }
                else
                    SendToPipe(dir.FullName);
            } //ends lock(syncobj)
            return pause;
        } //ends private void ReceiveDirInfo(...

        /// <summary>
        /// Called when TerminateAllThreads() completes
        /// </summary>
        /// <param name="ar"></param>
        private void TerminateThreadsCallback(IAsyncResult ar)
        {
            AsyncResult result = ar as AsyncResult;
            TerminateThreads resolve = result.AsyncDelegate as TerminateThreads;
            if (null != allThreadsTerminated)
                allThreadsTerminated.Set();
            resolve.EndInvoke(ar);
        } //ends private void TerminateThreadsCallback(...

        /// <summary>
        /// Called asynchronusly when the user has requested all threads terminate or is closing the main form.
        /// It is expected pleaseTerminate.Set(); has already been called.
        /// </summary>
        private void TerminateAllThreads()
        {
            int 
                alive,
                waitCount = 0;
            while (true)
            {
                do
                {   //give threads a change to terminate gracefully
                    Thread.Sleep(200);
                    Application.DoEvents();
                    alive = 0;
                    lock (threadobj)
                    {
                        foreach (Thread workThread in searchThreads)
                            if (workThread.IsAlive)
                                ++alive;
                    } //ends lock(threadobj)
                } while ((alive > 0) && (++waitCount < 5));
                alive = 0;
                Application.DoEvents();
                lock (threadobj)
                {   //kill any threads that have not terminated gracefully
                    foreach (Thread workThread in searchThreads)
                    {
                        if (workThread.IsAlive)
                        {
                            ++alive;
                            workThread.Abort();
                        }
                    }
                } //ends lock(threadobj)
                if (alive == 0)
                    break;
            } //ends while (true)
        } //ends private void TerminateAllThreads()

        /// <summary>
        /// Dynamic build of the grid view
        /// </summary>
        /// <param name="filesNeeded">Set to true if columns associated with files are to be built</param>
        private void BuildGridTable(bool filesNeeded)
        {   //create the grid table
            if (null != table)
            {
                SaveCurrentColumns();
                //already on the desired view?
                if ((filesNeeded && usingView.ViewName == "FilesView")
                || (!filesNeeded && usingView.ViewName == "FoldersView"))
                {
                    table.Clear();  //yes, just clear the table
                    return;
                }
                else
                    table = null;   //no, switch to correct view
            }
            if (null == table)
            {
                gridView.DataSource = null;             //release old table, if any
                gridView.BackgroundColor = listBox1.BackColor;
                gridView.BorderStyle = listBox1.BorderStyle;
                table = new DataTable("FileFind");
                if (filesNeeded)
                {
                    table.Columns.Add(new DataColumn(GTFILENAME));
                    table.Columns.Add(new DataColumn(GTSIZE, Type.GetType("System.Int64")));
                    foldersOnlyDisplay = false;
                }
                else
                    foldersOnlyDisplay = true;
                table.Columns.Add(new DataColumn(GTDISK));
                table.Columns.Add(new DataColumn(GTVOLUMEID));
                table.Columns.Add(new DataColumn(GTDIRECTORY));
                table.Columns.Add(new DataColumn(GTCREATED, Type.GetType("System.DateTime")));
                table.Columns.Add(new DataColumn(GTMODIFIED, Type.GetType("System.DateTime")));
                table.Columns.Add(new DataColumn(GTLASTACCESSED, Type.GetType("System.DateTime")));
                BindingSource bs = new BindingSource();
                bs.DataSource = table;
                gridView.DataSource = bs;
                if (filesNeeded)
                    gridView.Columns[GTSIZE].DefaultCellStyle.Format = "N0";
                gridView.Columns[GTCREATED].DefaultCellStyle.FormatProvider = CultureInfo.CurrentCulture;
                gridView.Columns[GTMODIFIED].DefaultCellStyle.FormatProvider = CultureInfo.CurrentCulture;
                gridView.Columns[GTLASTACCESSED].DefaultCellStyle.FormatProvider = CultureInfo.CurrentCulture;
            }
            ConfigInfo configInfo = new ConfigInfo();
            if (filesNeeded)
            {
                if (null == (usingView = filesView))
                {
                    usingView = filesView = configInfo.GetGridView("FilesView");
                    if (null == usingView)
                        usingView = filesView = BuildNewGridInfo("FilesView");
                }
            }
            else
            {
                if (null == (usingView = foldersView))
                {
                    usingView = foldersView = configInfo.GetGridView("FoldersView");
                    if (null == usingView)
                        usingView = foldersView = BuildNewGridInfo("FoldersView");
                }
            }

            //You must rearrange the grid starting with the first displayed column and
            //the grid must be visible otherwise the result is wrong.
            bool holdVisible = gridView.Visible;    //Columns will not reorder unless
            gridView.Visible = true;                //the grid is visible.
            usingView.SortByDisplayOrder();         //Put columns in display order.
            foreach (ColumnInfo columnInfo in usingView)
                gridView.Columns[columnInfo.ColumnName].DisplayIndex = columnInfo.DisplayOrder;
            gridView.Visible = holdVisible;

            foreach (DataGridViewColumn dgc in gridView.Columns)
            {
                if (GTDISK == dgc.Name)
                    dgc.AutoSizeMode = DataGridViewAutoSizeColumnMode.ColumnHeader;
                else
                    dgc.MinimumWidth = dgc.Width / 2;
                if (usingView.Contains(dgc.Name))
                    gridView.Columns[dgc.Name].Visible = usingView.Visible(dgc.Name);
            }
            foreach (ToolStripMenuItem menuItem in this.showGridColumnsToolStripMenuItem.DropDownItems)
            {   //update the 'Show grid columns' menu items according to the states saved in the configInfo
                if (usingView.Contains(menuItem.Text))
                {
                    menuItem.Enabled = true;
                    menuItem.Checked = usingView.Visible(menuItem.Text);
                }
                else
                    menuItem.Enabled = false;
            }
        } //ends private void BuildGridTable(...

        /// <summary>
        /// Saves column ordering information for the current grid view
        /// </summary>
        private void SaveCurrentColumns()
        {   //store current column display order
            usingView.IsDirty = true;
            DataGridViewColumnCollection columns = gridView.Columns;    //displayable grid columns
            for (int i = 0; i < columns.Count; ++i)
                usingView.UpdateDisplayOrder(columns[i].Name, columns[i].DisplayIndex);
        }

        /// <summary>
        /// Restores column display order
        /// </summary>
        /// <param name="name">View name</param>
        /// <returns>grid view with unwanted columns disabled</returns>
        private GridInfoCollection BuildNewGridInfo(string name)
        {
            DataGridViewColumnCollection columns = gridView.Columns;    //displayable grid columns
            GridInfoCollection newView = new GridInfoCollection(name);
            for (int i = 0; i < columns.Count; ++i)
                newView.Add(new ColumnInfo(columns[i].Name, true, columns[i].DisplayIndex, -1));
            return newView;
        }

        private void BuildGridColumnSelectionMenu()
        {   //build menu column selection
            ArrayList toolStripMenuItems = new ArrayList();
            toolStripMenuItems.Add(BuildMenuItem(GTFILENAME));
            toolStripMenuItems.Add(BuildMenuItem(GTSIZE));
            toolStripMenuItems.Add(BuildMenuItem(GTDISK));
            toolStripMenuItems.Add(BuildMenuItem(GTVOLUMEID));
            toolStripMenuItems.Add(BuildMenuItem(GTDIRECTORY));
            toolStripMenuItems.Add(BuildMenuItem(GTCREATED));
            toolStripMenuItems.Add(BuildMenuItem(GTMODIFIED));
            toolStripMenuItems.Add(BuildMenuItem(GTLASTACCESSED));
            System.Windows.Forms.ToolStripItem[] toolStripItems =
                (System.Windows.Forms.ToolStripMenuItem[])toolStripMenuItems.ToArray(typeof(System.Windows.Forms.ToolStripMenuItem));
            this.showGridColumnsToolStripMenuItem.DropDownItems.AddRange(toolStripItems);
        } //ends private void BuildGridColumnSelectionMenu(...

        private System.Windows.Forms.ToolStripMenuItem BuildMenuItem(string itemName)
        {
            System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem = new ToolStripMenuItem();
            StringBuilder sb = new StringBuilder(128);
            sb.Append(itemName.Trim());
            sb.Replace(' ', '_');
            fileToolStripMenuItem.Name = sb.ToString();
            fileToolStripMenuItem.Text = itemName;
            fileToolStripMenuItem.AutoSize =
            fileToolStripMenuItem.Checked = true;
            fileToolStripMenuItem.Enabled = false;
            fileToolStripMenuItem.Click += new System.EventHandler(this.ToolStripMenuItem_Click);
            return fileToolStripMenuItem;
        } //ends private System.Windows.Forms.ToolStripMenuItem BuildMenuItem(...

        private void ActivateListView()
        {   //swap to "List View"
            gridListViewButton.Text = MENUGRIDVIEW;
            if (0 == listBox1.Items.Count)
                sortOutputButton.Enabled = false;
            else
                sortOutputButton.Enabled = true;
            gridView.Visible = false;
            listBox1.Visible = true;
            sortOutputButton.Text = SORTOUTPUT;
        } //ends private void ActivateListView()

        private void ActivateGridView()
        {   //swap to "Grid View"
            sortOutputButton.Text = DELETE;
            gridListViewButton.Text = MENULISTVIEW;
            if (0 == gridView.RowCount)
                sortOutputButton.Enabled = false;
            else
                sortOutputButton.Enabled = true;
            listBox1.Visible = false;
            gridView.Visible = true;
            gridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells);
            //The above gridView.AutoResizeColumns may cause long pauses when large numbers of
            //entries are in the gridView
        } //ends private void ActivateGridView()

        /// <summary>
        /// Delete one or more data rows and the files or folders named in the rows
        /// </summary>
        private void UserRequestedDelete()
        {   
            fileCountLabel.Text =
            msgLabel1.Text =
            msgLabel2.Text = "";
            int itemsDeleted = 0;
            DataGridViewSelectedRowCollection dgv = gridView.SelectedRows;
            bool
                hasFileNameTitle = false,          //determine if the 'File name' column exists
                foundReparsePoint = false;
            foreach (DataColumn dc in table.Columns)
                if (dc.ColumnName.ToString() == GTFILENAME)
                {
                    hasFileNameTitle = true;
                    break;
                }
            table.BeginLoadData();
            foreach (DataGridViewRow deleteRow in dgv)
            {   //delete the selected file/directory 
                Int32 index = deleteRow.Index;
                string dirName = Path.Combine(gridView.Rows[index].Cells[GTDISK].Value.ToString(),
                    gridView.Rows[index].Cells[GTDIRECTORY].Value.ToString());
                string fname = "";
                if(hasFileNameTitle)
                    fname = gridView.Rows[index].Cells[GTFILENAME].Value.ToString();
                if (String.IsNullOrEmpty(fname))
                {   //delete a directory
                    if (Directory.Exists(dirName))
                    {
                        DirectoryInfo processDirectory = new DirectoryInfo(dirName);
                        if ((processDirectory.Attributes & FileAttributes.ReparsePoint)
                            == FileAttributes.ReparsePoint)
                            foundReparsePoint = true;
                        try
                        {
                            FileSystem.DeleteDirectory(dirName,
                                UIOption.OnlyErrorDialogs,
                                RecycleOption.SendToRecycleBin);
                            listBox1.Items.Remove(dirName);    //keep list & grid in sync
                            gridView.Rows.RemoveAt(index);
                            msgLabel1.Text = "Deleted " + dirName;
                            ++itemsDeleted;
                        }
                        catch (Exception ex)
                        { msgLabel1.Text = ex.Message; }
                        //remove listed members that were in the directory
                        string removedDirectory = dirName + "\\";
                        int i = 0;
                        Stack myStack = new Stack();
                        foreach (string listItem in listBox1.Items)
                        {
                            string displayedDirectory = listItem.ToString();
                            if (displayedDirectory.Length < removedDirectory.Length)
                                continue;
                            if (string.Compare(removedDirectory,
                                displayedDirectory.Substring(0, removedDirectory.Length),
                                true, CultureInfo.InvariantCulture) == 0)
                                myStack.Push(i);
                            ++i;
                        }
                        listBox1.BeginUpdate();
                        try
                        {
                            while (true)
                            {
                                int j = (int)myStack.Pop();
                                listBox1.Items.RemoveAt(j);
                            }
                        }
                        catch (InvalidOperationException) { }
                        listBox1.EndUpdate();
                        for (int ndx = 0; ndx <= gridView.Rows.Count - 1; ++ndx)
                        {
                            string displayedDirectory = Path.Combine(gridView.Rows[ndx].Cells[GTDISK].Value.ToString(),
                                gridView.Rows[ndx].Cells[GTDIRECTORY].Value.ToString());
                            displayedDirectory += "\\";
                            if (displayedDirectory.Length < removedDirectory.Length)
                                continue;
                            if (string.Compare(removedDirectory,
                                    displayedDirectory.Substring(0, removedDirectory.Length),
                                    true, CultureInfo.InvariantCulture) == 0)
                                myStack.Push(ndx);
                        }
                        try
                        {
                            while (true)
                            {
                                int j = (int)myStack.Pop();
                                gridView.Rows.RemoveAt(j);
                            }
                        }
                        catch (InvalidOperationException) { }
                    } //ends if (Directory.Exists(...
                }
                else
                {   //delete a file
                    string fileFullName = Path.Combine(dirName, fname);
                    if (File.Exists(fileFullName))
                    {
                        try
                        {
                            FileSystem.DeleteFile(fileFullName,
                                UIOption.OnlyErrorDialogs,
                                RecycleOption.SendToRecycleBin);
                            listBox1.Items.Remove(fileFullName);   //keep list & grid in sync
                            gridView.Rows.RemoveAt(index);
                            msgLabel1.Text = "Deleted " + fileFullName;
                            ++itemsDeleted;
                        }
                        catch (Exception ex)
                        { msgLabel1.Text = ex.Message; }
                    }
                }
            } //ends foreach (DataGridViewRow deleteRow...
            table.EndLoadData();
            fileCountLabel.Text = itemsDeleted.ToString("N0", CultureInfo.CurrentCulture) + " items deleted";
            if (foundReparsePoint)
                msgLabel2.Text = "Use Computer Manage function to remove reparse points";
        } //ends private void UserRequestedDelete()

        private void Form1_Load(object sender, EventArgs e)
        {   // initialize delegates
            m_DelegateSendMsg = new DelegateSendMsg(this.SendMsg);
            m_DelegateThreadStopped = new DelegateThreadStopped(this.ThreadStopped);
            m_SendFileInfo = new SendFileInfo(this.ReceiveFileInfo);
            m_SendDirectoryInfo = new SendDirectoryInfo(this.ReceiveDirInfo);
            m_DelegateUpdateStatus = new DelegateUpdateStatus(this.UpdateStatus);
            //acquire configuration information
            ConfigInfo configInfo = new ConfigInfo();
            if (null == configInfo.UseFont)             //prior font in use?
                configInfo.UseFont = this.Font;         // no, save current font
            else
                this.Font = configInfo.UseFont;         // yes, change to the prior font
            toolStripStatusLabel1.Text = "";
            //setup filter list constraints
            FilterList usingFilterList = null;
            try { usingFilterList = configInfo.ActiveFilterList; }
            catch { }
            HandleDriveSelection();                     //starts async drive resolution
            if (null != usingFilterList)
                filterLabel.Text = "Using filter list " + usingFilterList.Name;
            else
                filterLabel.Text = "No filter list";
            VolumeLabelDrive volumeLabels = new VolumeLabelDrive();
            if (volumeLabels.HasDuplicates || volumeLabels.HasUnlabeledVolumes)
                if (volumeLabels.HasDuplicates && volumeLabels.HasUnlabeledVolumes)
                    filterLabel.Text += "; Caution: unlabeled drives or duplicate volume labels were found";
                else if (volumeLabels.HasDuplicates)
                    filterLabel.Text += "; Caution: duplicate volume labels were found";
                else if (volumeLabels.HasUnlabeledVolumes)
                    filterLabel.Text += "; Caution: unlabeled drives were found";
                    
            //provide initial instructions and complete the initial form
            fileCountLabel.Text = "Enter one or more masks to search for file and/or folder names.";
            msgLabel1.Text = "Use '?' to match any single character. Use '*' to match one or more characters.";
            msgLabel2.Text = "";
            //set prior menu values
            showHiddenFoldersfilesToolStripMenuItem.Checked = configInfo.ShowHiddenFoldersFiles;
            scanningViaEnumeratorToolStripMenuItem.Checked = configInfo.ScanningViaEnumerator;
            autoScrollMode =
            autoScrollingToolStripMenuItem.Checked = configInfo.AutoScrolling;
            BuildGridColumnSelectionMenu();             //add available columns to menu bar
            BuildGridTable(true);
            gridView.Bounds = listBox1.Bounds; //make list and grid viewing areas the same
            if (configInfo.StartWithGrid)
            {
                this.Width += this.Width / 4;   //increase form width by 25%
                ActivateGridView();
                msgLabel2.Text = "Double click file name to launch file or directory name to open folder.";
            }
            else
            {
                ActivateListView();
                msgLabel2.Text = "Double click name to launch file or open folder.";
            }
            startStopButton.Enabled = false;
            Application.Idle += new System.EventHandler(OnIdle);
            fileNameBox.Select();       //put focus to fileNameBox
            priorTime = DateTime.Now;
            dateTimeInfo = new DateTimeInfo();

            if (0 != Program.parametersReceived.Length)
            {   //parameters were passed to this program
                //validate the parameters and connect to a pipe for output
                string filterListName = "";
                string pipeName = "";
                string masks = "";
                string serverName = ".";
                bool showForm = false;
                foreach (string str in Program.parametersReceived)
                    if (str.StartsWith("-FilterList:", StringComparison.InvariantCultureIgnoreCase))
                        filterListName = str.Substring("-FilterList:".Length);
                    else if (str.StartsWith("-PipeName:", StringComparison.InvariantCultureIgnoreCase))
                        pipeName = str.Substring("-PipeName:".Length);
                    else if (str.StartsWith("-Masks:", StringComparison.InvariantCultureIgnoreCase))
                        masks = str.Substring("-Masks:".Length);
                    else if (str.StartsWith("-FilesFolders:"))
                    {
                        string searchingFor = str.Substring("-FilesFolders:".Length).ToLower(CultureInfo.InvariantCulture);
                        switch (searchingFor)
                        {
                            case "files":
                                filesOnlyRadioButton.Checked = true;
                                break;
                            case "folders":
                                foldersOnlyRadioButton.Checked = true;
                                break;
                            case "both":
                                filesAndFoldersRadioButton.Checked = true;
                                break;
                        }
                    }
                    else if (str.StartsWith("-Visible:", StringComparison.InvariantCultureIgnoreCase))
                        showForm = (0 == String.Compare(str.Substring("-Visible:".Length), "Yes", 
                            true, CultureInfo.InvariantCulture));
                if (!String.IsNullOrEmpty(pipeName)
                && !String.IsNullOrEmpty(masks))
                {
                    if (!String.IsNullOrEmpty(filterListName))
                    {
                        FilterListCollection filterLists;
                        if (null != (filterLists = configInfo.MasterFilterListCollection))
                        {
                            FilterList filterList = filterLists.LocateFilterList(filterListName);
                            if (null != filterList)
                            {
                                configInfo.ActiveFilterList = filterList;
                                filterLabel.Text = "Using filter list " + filterList.Name;
                            }
                            else
                            {
                                fileCountLabel.Text = "Could not find filter list " + filterListName;
                                filterLabel.Text = "No filter list is being used";
                                showForm = true;
                            }
                        }
                    }
                    msgLabel1.Text = "-FilterList:" + filterListName;
                    msgLabel2.Text = "-PipeName:" + pipeName;
                    fileNameBox.Text = masks;
                    UpdateStatus("Connecting to " + pipeName);
                    Application.DoEvents();
                    pipe = new NamedPipeClientStream(serverName, pipeName, PipeDirection.Out, PipeOptions.None);
                    try { pipe.Connect(3000); }
                    catch (TimeoutException)
                    { fileCountLabel.Text = "Connection timed out for pipe " + pipeName; }
                    if (pipe.IsConnected && pipe.CanWrite)
                    {   //pipe connection is valid
                        gridListViewButton.Enabled =
                        filesAndFoldersRadioButton.Enabled =
                        foldersOnlyRadioButton.Enabled =
                        filesOnlyRadioButton.Enabled = false;
                        UpdateStatus("Pipe connection established");
                        fileCountLabel.Text = "Connected to pipe " + pipeName;
                        startStopButton.Enabled =
                        this.ShowInTaskbar =
                        this.Visible = showForm;
                        Application.DoEvents(); //allow form to display
                        StartSearching();
                    }
                    else
                    {   //pipe connection failed
                        try
                        { pipe.Close(); }
                        catch { }
                        this.Close();   //close the FileFind application
                    }
                }
                else
                {
                    msgLabel1.Text = "-FilterList:" + filterListName;
                    msgLabel2.Text = "-PipeName:" + pipeName;
                    fileNameBox.Text = masks;
                    fileCountLabel.Text = "Invalid arguments were received at startup";
                }
            }
        } //ends private void Form1_Load(...

        /// <summary>
        /// Enables the Start/Stop button only after a mask has been entered
        /// </summary>
        private void OnIdle(Object sender, EventArgs e)
        { startStopButton.Enabled = fileNameBox.Text.Length > 0; }

        private void Form1_Closing(object sender, FormClosingEventArgs e)
        {   // Request termination of any active threads.
            // Store the current view in the ConfigInfo object 
            //and save the configuration data for the next time this application is run.
            // Abort any threads still running.
            Application.Idle -= new System.EventHandler(OnIdle);
            timer1.Enabled = false;
            allThreadsTerminated = new ManualResetEvent(false);
            pleaseTerminate.Set();    //request threads stop
            startStopButton.Text = SEARCHSTOPPING;
            //save information needed for next execution
            ConfigInfo configInfo = new ConfigInfo();
            if (MENULISTVIEW == gridListViewButton.Text)
                configInfo.StartWithGrid = true;
            else
                configInfo.StartWithGrid = false;
            if (null != usingView)
            {
                SaveCurrentColumns();
                if (null != filesView && filesView.IsDirty)
                    configInfo.StoreGridView(filesView);
                if (null != foldersView && foldersView.IsDirty)
                    configInfo.StoreGridView(foldersView);
            }
            configInfo.SaveConfig();
            Application.DoEvents();
            if (0 != searchThreads.Count)
            {   //some threads are still active
                TerminateThreads terminateThreads = new TerminateThreads(TerminateAllThreads);
                terminateThreads.BeginInvoke(new AsyncCallback(TerminateThreadsCallback), null);
                while(allThreadsTerminated.WaitOne(200, false))
                    Application.DoEvents();
            } //ends if (0 != searchThreads.Count)
            if (null != pipe)
            {   //need to close the pipe
                try { pipe.Close(); }
                catch { }
            }
        } //ends private void Form1_Closing(...

        /// <summary>
        /// Enable or disable various buttons
        /// </summary>
        /// <param name="setting">Set to true to enable, false to disable</param>
        private void SearchingButtons(bool setting)
        {   //enable or disable various buttons and menu selections
            if (null != pipe)
                return;
            listBox1.Sorted =          
            timeRangeButton.Enabled =
            disksToSearchButton.Enabled =
            FilterButton.Enabled =
            fileNameBox.Enabled =
            startStopButton.Enabled =
            sortOutputButton.Enabled =
            sequentialSearchToolStripMenuItem.Enabled = setting;
        }

        /// <summary>
        /// StartSearching will start a thread for each selected disk.
        /// The DiskScan thread will search the disks for matching files and directory nodes
        /// </summary>
        private void StartSearching()
        {   //insure a mask has been provided
            //reset statistics
            //start a thread for each selected disk to search for matching files/directories
            if (null == pipe)
            {   //clear text messages
                msgLabel1.Text =
                msgLabel2.Text =
                fileCountLabel.Text = "";
                SearchingButtons(false);
            }
            WaitForResolvedCompletion();
            string fileMasks = fileNameBox.Text.Trim();   //remove leading and trailing white space
            if (String.IsNullOrEmpty(fileMasks))
            {
                ConfigInfo configInfo = new ConfigInfo();
                MessageBox.Show("You must enter one or more search masks", Application.ProductName,
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                SearchingButtons(true);
                return;
            }
            //for each disk set, start a thread to search disks for files and/or folders matching the mask(s)
            if (0 == resolvedDrives.Count)
                msgLabel2.Text = "No disks were selected for searching";
            else
                lock (threadobj)
                {   //lock prevents concurrent update of searchThreads until all threads are started
                    itemsFound =                    //reset statistics
                    directoriesFound =
                    filesFound = 0;
                    priorTime = DateTime.Now;       //for messages per interval measurements
                    listBox1.Items.Clear();         //clear the list view data
                    table.Clear();                  //clear the grid view data

                    pleaseTerminate.Reset();        //insure threads are enabled to run status

                    if (foldersOnlyRadioButton.Checked)
                        BuildGridTable(false);      //drop file name and size grid columns
                    else
                        BuildGridTable(true);       //display all grid columns

                    startStopButton.Text = STOPSEARCH;
                    searchThreads = new List<Thread>(resolvedDrives.Count);    // + networkPaths.Count);
                    elaspedTime.Reset();
                    elaspedTime.Start();
                    foreach (String resolvedDrive in resolvedDrives)
                    {   // create worker thread instance
                        DiskScan diskScan = new DiskScan(this,
                            m_DelegateSendMsg, m_SendDirectoryInfo, m_SendFileInfo, 
                            pleaseTerminate, m_DelegateThreadStopped, dateTimeInfo);
                        diskScan.SearchingDrives = resolvedDrive;
                        if (filesOnlyRadioButton.Checked)
                            diskScan.SearchForFiles = true;
                        else if (foldersOnlyRadioButton.Checked)
                            diskScan.SearchForFolders = true;
                        else if (filesAndFoldersRadioButton.Checked)
                            diskScan.SearchForBoth = true;
                        diskScan.IFindFiles = fileMasks;
                        Thread searchThread = new Thread(new ThreadStart(diskScan.Run));
                        searchThread.Name = resolvedDrive;
                        searchThreads.Add(searchThread);
                        searchThread.Start();
                    } //ends foreach(String resolvedDrive in...
                    startStopButton.Enabled = true;
                    BuildThreadStatusLine();
                } //ends lock (threadobj)
        } //ends StartSearching()

        private void HandleDriveSelection()
        {   //populate the list of disks to search
            //output is saved to global 'selectedDisks'
            //An asynchronous disk resolution is started by this routine
            int diskCount = 0;
            ArrayList localSelectedDisks = FetchAllDisks(out diskCount);
            switch (diskCount)
            {
                case -1:
                    disksToSearchButton.Text = SEARCHALLDISKS;
                    break;
                case 0:
                    disksToSearchButton.Text = SEARCHNONE;
                    break;
                default:
                    disksToSearchButton.Text = SEARCHSELECTEDDISKS;
                    break;
            }
            if (0 == localSelectedDisks.Count)
                msgLabel2.Text = "Unable to find any disks on this system";
            selectedDisks = localSelectedDisks;
        } //ends private void HandleDriveSelection()

        /// <summary>
        /// fetchs all logical ready drives on the system
        /// and starts logical to physical resolution
        /// </summary>
        /// <param name="diskCount">
        /// Returns number of drives selected else -1 then all ready disks have been selected.</param>
        /// <returns>returns an array of the names of logical drives on the system</returns>
        private ArrayList FetchAllDisks(out int diskCount)
        {
            ConfigInfo configInfo = new ConfigInfo();
            ArrayList selected = configInfo.SelectAllDisks(out diskCount);
            StartResolution(selected);
            return selected;
        } //ends private ArrayList FetchAllDisks(...

        /// <summary>
        /// fetchs an array of logical drives to be searched
        /// and starts logical to physical resolution
        /// </summary>
        /// <returns>returns an array of the names of logical drives to be searched</returns>
        private ArrayList FetchSelectedDisks(ArrayList selected)
        {
            StartResolution(selected);
            return selected;
        }//ends private ArrayList FetchSelectedDisks()

        /// <summary>
        /// Starts an async thread to assign logical disk partitions to physical drives
        /// </summary>
        /// <param name="selected">names of logical drives to resolve</param>
        private void StartResolution(ArrayList selected)
        {
            //wait for any other drive resolution threads to complete
            Interlocked.Increment(ref resolutionsScheduled);
            if (resolvedInUse.WaitOne(0, true))
            {
                this.Cursor = Cursors.WaitCursor;
                UpdateStatus("Waiting on logical/physical drive resolution");
                resolvedInUse.WaitOne();
                this.Cursor = Cursors.Default;
            }
            resolvedInUse.Set();
            UpdateStatus("Starting logical/physical disk resolution");
            resolveLogicalPhysicalDisks.BeginResolveDrives(selected, ResolutionCompleted);
        } //ends private void StartResolution()

        private void ResolutionCompleted(ArrayList drives)
        {   //Caution: runs asynchronously under the resolveLogicalPhysicalDisks Callback thread
            //results are stored in global 'resolvedDrives'
            resolvedDrives = drives;
            if (performSequentialSearch)
            {   //assign all drives to the same DiskScan thread
                string[] resolvedDrivesArray = (string[])resolvedDrives.ToArray(typeof(string));
                resolvedDrives = new ArrayList();
                resolvedDrives.Add(String.Join(";", resolvedDrivesArray));
            }
            //disk resolution has completed, display resolution information
            StringBuilder sb = new StringBuilder(128);
            foreach (string diskset in resolvedDrives)
            {
                if (diskset.Length > 3)
                    sb.Append(" (" + diskset + ")");
                else
                    sb.Append(" " + diskset);
            }
            //if (0 != networkPathCount)
            //    sb.Append(" " +networkPathCount.ToString("n0", CultureInfo.CurrentCulture) + " net paths");
            UpdateStatus("Resolved " + sb.ToString());
            Interlocked.Decrement(ref resolutionsScheduled);
            resolvedInUse.Reset();
        }

        /// <summary>
        /// Waits for disk resolution completion
        /// </summary>
        private void WaitForResolvedCompletion()
        {
            if (!resolveLogicalPhysicalDisks.ResolutionComplete)
                do
                {
                    this.Cursor = Cursors.WaitCursor;
                    UpdateStatus("Waiting on logical/physical drive resolution");
                    resolveLogicalPhysicalDisks.ResolveEvent.WaitOne();
                    this.Cursor = Cursors.Default;
                } while (0 != resolutionsScheduled);
        } //ends private void WaitForResolvedCompletion()

        /// <summary>
        /// Called asynchronously when a thread terminates. Removes the thread from the
        /// searchThreads ArrayList and, if no active tasks are running, reports
        /// search completion and enables appropriate buttons.
        /// Also updates the status line.
        /// </summary>
        /// <param name="threadName">name of terminating thread</param>
        private void ThreadStopped(string threadName)
        {   //Invoked by a thread just before it terminates
            lock (threadobj)
            {
                int ndx = 0;
                foreach (Thread searchThread in searchThreads)
                    if (searchThread.Name == threadName)
                    {
                        searchThreads.RemoveAt(ndx);
                        break;
                    }
                    else
                        ++ndx;
                if (0 == searchThreads.Count)
                {   //all threads have completed, report stats & enable buttons
                    elaspedTime.Stop();
                    if (null != pipe)
                    {   //Running with a pipe, stop the FileFind program
                        try { pipe.Close(); }   //close the pipe
                        catch { }
                        this.Close();       //close the FileFind application
                    }
                    else
                    {
                        if (autoScrollMode)
                            listBox1.TopIndex = listBox1.Items.Count - 1;
                        if (filesAndFoldersRadioButton.Checked)
                            fileCountLabel.Text = ITEMSFOUND + itemsFound.ToString("N0", CultureInfo.CurrentCulture)
                                + " ( " + filesFound.ToString("N0", CultureInfo.CurrentCulture) + " files and "
                                + directoriesFound.ToString("N0", CultureInfo.CurrentCulture) + " directories )";
                        else
                            fileCountLabel.Text = ITEMSFOUND + itemsFound.ToString("N0", CultureInfo.CurrentCulture);
                        timeRangeButton.Enabled =
                        disksToSearchButton.Enabled =
                        FilterButton.Enabled =
                        fileNameBox.Enabled = true;
                        gridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                        if (gridView.Visible && (gridView.RowCount > 0))
                            sortOutputButton.Enabled = true;
                        else if (listBox1.Items.Count > 0)
                            sortOutputButton.Enabled = true;
                        if (pleaseTerminate.WaitOne(0, true))
                            UpdateStatus("Termination request completed, search time: "
                                + elaspedTime.Elapsed.ToString());
                        else
                            UpdateStatus("Search completed, search time: "
                                + elaspedTime.Elapsed.ToString());
                        startStopButton.Text = STARTSEARCH;
                    }
                }
                else
                    BuildThreadStatusLine();
            } //ends lock (threadobj)
        } //ends private void ThreadStopped(...

        /// <summary>
        /// Builds and updates the status line. Caller must own a lock on threadobj.
        /// </summary>
        private void BuildThreadStatusLine()
        {
            //int networkPathCount = 0;   //number of network paths being searched
            StringBuilder sb = new StringBuilder(128);
                foreach (Thread searchThread in searchThreads)
                {
                    if (searchThread.Name.Length > 3)
                        sb.Append(" (" + searchThread.Name + ")");  //multi-partition drive
                    else
                        sb.Append(" " + searchThread.Name); //single drive
                }
                //if (0 != networkPathCount)
                //    sb.Append(" " + networkPathCount.ToString("n0", CultureInfo.CurrentCulture) + " net paths");
                UpdateStatus("Searching " + searchThreads.Count + " disks: " + sb.ToString());
        } //ends private void BuildThreadStatusLine()

        #region Mouse click processing
        private void GetFileStats(FileInfo fileInfo)
        {   //gather and display information about a selected file
            System.Text.StringBuilder sb = new System.Text.StringBuilder(256);
            if ((fileInfo.Attributes & FileAttributes.Compressed) == FileAttributes.Compressed)
                ReportAttributes(ref sb, "Compressed");
            if ((fileInfo.Attributes & FileAttributes.Encrypted) == FileAttributes.Encrypted)
                ReportAttributes(ref sb, "Encrypted");
            if ((fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
                ReportAttributes(ref sb, "Hidden");
            if ((fileInfo.Attributes & FileAttributes.Offline) == FileAttributes.Offline)
                ReportAttributes(ref sb, "Offline");
            if ((fileInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                ReportAttributes(ref sb, "ReadOnly");
            if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
                ReportAttributes(ref sb, "ReparsePoint");
            if ((fileInfo.Attributes & FileAttributes.System) == FileAttributes.System)
                ReportAttributes(ref sb, "System");
            if ((fileInfo.Attributes & FileAttributes.SparseFile) == FileAttributes.SparseFile)
                ReportAttributes(ref sb, "SparseFile");
            if (!String.IsNullOrEmpty(sb.ToString()))
                sb.Append(" )");
            msgLabel1.Text = "File size: "
                + NumericScaling.DisplayScaledNamesBytes(fileInfo.Length)
                + sb.ToString();
            msgLabel2.Text = "Created: " + fileInfo.CreationTime;
        } // ends GetFileStats(...

        private void GetFolderStats(FileInfo fileInfo)
        {   //gather and display information about a selected folder
            string dirStats = null;
            DirectoryInfo dirInfo = new DirectoryInfo(fileInfo.FullName);
            if (dirInfo.Exists)
            {
                long files = 0;
                long directories = 0;
                // Call the GetFileSystemInfos method.
                FileSystemInfo[] infos = dirInfo.GetFileSystemInfos();
                try
                {
                    foreach (FileSystemInfo fileSystemInfo in infos)
                    {
                        if (fileSystemInfo is DirectoryInfo) // is this is a DirectoryInfo object?
                            ++directories;
                        else if (fileSystemInfo is FileInfo) // is this is a FileInfo object?
                            ++files;
                    }
                }
                catch (Exception ex)
                { msgLabel1.Text = ex.Message; msgLabel2.Text = null; }
                if (0 == (directories + files))
                    dirStats = " ( Empty )";
                else
                    dirStats = " contains " + files.ToString("N0", CultureInfo.CurrentCulture) + " files and "
                        + directories.ToString("N0", CultureInfo.CurrentCulture) + " folders";
            } //ends if(dirInfo.Exists)
            System.Text.StringBuilder sb = new System.Text.StringBuilder(128);
            if ((dirInfo.Attributes & FileAttributes.Compressed) == FileAttributes.Compressed)
                ReportAttributes(ref sb, "Compressed");
            if ((dirInfo.Attributes & FileAttributes.Encrypted) == FileAttributes.Encrypted)
                ReportAttributes(ref sb, "Encrypted");
            if ((dirInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
                ReportAttributes(ref sb, "Hidden");
            if ((dirInfo.Attributes & FileAttributes.Offline) == FileAttributes.Offline)
                ReportAttributes(ref sb, "Offline");
            if ((dirInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                ReportAttributes(ref sb, "ReadOnly");
            if ((dirInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint)
                ReportAttributes(ref sb, "ReparsePoint");
            if ((dirInfo.Attributes & FileAttributes.System) == FileAttributes.System)
                ReportAttributes(ref sb, "System");
            if (!String.IsNullOrEmpty(sb.ToString()))
                sb.Append(" )");
            msgLabel1.Text = "Folder" + dirStats
                + sb.ToString();
            msgLabel2.Text = "Created: " + fileInfo.CreationTime;
        } //ends private void GetFolderStats(...

        private void ReportAttributes(ref StringBuilder sb, string str)
        {   //Used by GetFileStats and GetFolderStats
            if (String.IsNullOrEmpty(sb.ToString()))
                sb.Append(" (");
            sb.Append(" " + str);
        } //ends private void ReportAttributes(...
        
        private void FileLaunch(object sender, MouseEventArgs e)
        {   //a listBox item was double clicked
            //get the selected item and launch the program or open the folder or disk volume
            string str;
            try { str = listBox1.SelectedItem.ToString(); }
            catch (NullReferenceException)
            { return; }
            try
            {
                FileInfo fileInfo = new FileInfo(str);
                if ((fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                    GetFolderStats(fileInfo);
                else
                    GetFileStats(fileInfo);
                if (!PerformLaunch(fileInfo.FullName))
                    msgLabel1.Text = "Unable to launch " + fileInfo.FullName;
            }
            catch (Exception ex)
            { msgLabel1.Text = ex.Message; msgLabel2.Text = null; }
        } //ends private void FileLaunch(...
        
        private bool PerformLaunch(string parm)
        {   //start a process using the parameter as a command line
            bool PerformLaunch_result = true;
            try
            {
                Process p = new Process();
                p.StartInfo.FileName = parm;
                p.StartInfo.Arguments = null;
                p.Start();
            }
            catch (Exception ex)
            {
                string str = "Launch failed for\n" + parm + "\n"
                    + ex.Message;
                ConfigInfo configInfo = new ConfigInfo();
                MessageBox.Show(str, Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                PerformLaunch_result = false;
            }
            return PerformLaunch_result;
        }   //ends private bool PerformLaunch

        /// <summary>
        /// Display file or directory information in the message area
        /// </summary>
        private void FilesFoundBox_MouseClick(object sender, MouseEventArgs e)
        {   //single mouse click in listBox
            //determine if file or folder and report appropriate stats
            string str;
            try
            { str = listBox1.SelectedItem.ToString(); }
            catch (NullReferenceException)
            { return; }
            if ((!Directory.Exists(str)) && (!File.Exists(str)))
            {
                msgLabel1.Text = "File or directory not found";
                msgLabel2.Text = null;
                return;
            }
            try
            {
                FileInfo fileInfo = new FileInfo(str);
                if ((fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                    GetFolderStats(fileInfo);
                else
                    GetFileStats(fileInfo);
            }
            catch (Exception ex)
            { msgLabel1.Text = ex.Message; msgLabel2.Text = null; }
        } //ends private void FilesFoundBox_MouseClick

        /// <summary>
        /// Display drive information in the message area
        /// </summary>
        /// <param name="diskName">name of disk</param>
        private void DisplayDriveInfo(string diskName)
        {   
            fileCountLabel.Text = "";
            DriveInfo driveInfo = new DriveInfo(diskName);
            if (driveInfo.IsReady)
            {
                msgLabel1.Text = diskName + " " + driveInfo.VolumeLabel
                    + "  Drive format: " + driveInfo.DriveFormat
                    + "  Drive type: " + driveInfo.DriveType;
                long availableFreeSpace = driveInfo.AvailableFreeSpace;
                long totalSize = driveInfo.TotalSize;
                msgLabel2.Text = "Available space: " + NumericScaling.DisplayScaledNames(availableFreeSpace)
                    + "  Total size: " + NumericScaling.DisplayScaledNames(totalSize);
            }
            else
            {
                msgLabel1.Text = diskName + " is not ready";
                msgLabel2.Text = "";
            }
        } //ends private void DisplayDriveInfo(...

        private void CellContentClicked(object sender, DataGridViewCellEventArgs e)
        {   //single mouse click in gridView
            //determine if file or folder and report appropriate stats
            if (-1 == e.RowIndex)   //was title selected?
                return;             // yes
            fileCountLabel.Text = "";
            List<string> columnTitles = new List<string>(table.Columns.Count);   //array of string containing column names
            foreach (DataColumn dc in table.Columns)
                columnTitles.Add(dc.ColumnName.ToString());
            DisplayFileFolderInfo(columnTitles[e.ColumnIndex].ToString(), e.RowIndex);
        } //ends private void CellContentClicked(...

        private void CellContentDoubleClicked(object sender, DataGridViewCellEventArgs e)
        {   //double mouse click in gridView
            //determine if file or folder and launch appropriately
            if (-1 == e.RowIndex)   //was title selected?
                return;             // yes, ignore it
            fileCountLabel.Text = "";
            List<string> columnTitles = new List<string>(table.Columns.Count);   //array of string containing column names
            foreach (DataColumn dc in table.Columns)    //populate the array
                columnTitles.Add(dc.ColumnName.ToString());
            FileFolderLaunch(columnTitles[e.ColumnIndex].ToString(), e.RowIndex);
        } //ends private void CellContentDoubleClicked(...
        #endregion Mouse click processing

        #region Button processing
        private void startStopButton_Click(object sender, EventArgs e)
        {   //display verbage toggles between Start Search and Stop Search
            //Start or stop the search threads
            lock (threadobj)
            {
                if (STOPSEARCH == startStopButton.Text)
                {
                    pleaseTerminate.Set();
                    startStopButton.Text = SEARCHSTOPPING;
                }
                else if (SEARCHSTOPPING == startStopButton.Text)
                {
                    pleaseTerminate.Set();
                    TerminateThreads terminateThreads = new TerminateThreads(TerminateAllThreads);
                    terminateThreads.BeginInvoke(new AsyncCallback(TerminateThreadsCallback), null);
                }
                else
                    StartSearching();
            } //ends lock (threadobj)
        }

        private void sortOutputButton_Click(object sender, EventArgs e)
        {   //toggled between Sort Output in List View and Delete in grid view
            if (sortOutputButton.Text == DELETE)
                UserRequestedDelete();
            else
            {
                this.Cursor = Cursors.WaitCursor;
                listBox1.Sorted = true;
                this.Cursor = Cursors.Default;
            }
        }

        private void disksToSearchButton_Click(object sender, EventArgs e)
        {   //Display a form to allow the user to select the disks to be searched
            using (SelectDisksToSearch diskSelection = new SelectDisksToSearch(selectedDisks))
            {
                diskSelection.FormLocation = this.Location;
                diskSelection.ShowDialog();
                if (DialogResult.Cancel != diskSelection.DialogResult)
                {
                    switch (diskSelection.DialogResult)
                    {
                        case DialogResult.Abort:     //no drives were selected
                            MessageBox.Show("No disks were selected", Application.ProductName,
                                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            selectedDisks = new ArrayList();
                            disksToSearchButton.Text = SEARCHNONE;
                            break;
                        case DialogResult.Yes:      //All drives were selected
                            disksToSearchButton.Text = SEARCHALLDISKS;
                            selectedDisks = FetchSelectedDisks(diskSelection.SelectedDisks);
                            break;
                        case DialogResult.No:       //some drives were selected
                            disksToSearchButton.Text = SEARCHSELECTEDDISKS;
                            selectedDisks = FetchSelectedDisks(diskSelection.SelectedDisks);
                            break;
                        default:
                            MessageBox.Show("Unknown Dialog result for disk selection", Application.ProductName,
                                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            break;
                    }
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Searching disks:");
                    foreach (String diskletter in selectedDisks)
                        sb.Append("  " + diskletter);
                    fileCountLabel.Text =
                    msgLabel1.Text = "";
                    msgLabel2.Text = sb.ToString();
                }
            }
        } //ends private void disksToSearchButton_Click(...

        private void FileNameBoxKeyPressed(object sender, KeyPressEventArgs e)
        {   //key was pressed while focus was in FileNameBox
            if ((char)Keys.Enter == e.KeyChar)
            {   //treat as Search button
                e.Handled = true;
                StartSearching();
                fileNameBox.Focus();
            }
        }

        private void timeRangeButton_Click(object sender, EventArgs e)
        {   //Display a form allowing user to select time ranges
            using (SelectDateTimeRange selectDateTimeRange = new SelectDateTimeRange())
            {
                selectDateTimeRange.FormLocation = this.Location;
                selectDateTimeRange.SelectedDateTimeRange = (DateTimeInfo)dateTimeInfo.Clone();
                DialogResult dateTimeRangeResult = selectDateTimeRange.ShowDialog();
                if (DialogResult.OK == dateTimeRangeResult)
                    dateTimeInfo = selectDateTimeRange.SelectedDateTimeRange;
            }
            DateTime fromDateTime = dateTimeInfo.IFromDateTime;
            DateTime toDateTime = dateTimeInfo.IToDateTime;
            msgLabel1.Text =
            msgLabel2.Text =
            fileCountLabel.Text = "";
            if (dateTimeInfo.IDateTimeRange == DateTimeInfo.DateTimeRangeSearch.DateTimeRangeOff)
                timeRangeButton.Text = SELECTTIMERANGE;
            else
            {   //display selected times in message area
                timeRangeButton.Text = SELECTEDTIMERANGE;
                msgLabel1.Text = "Selection time range is "
                + fromDateTime.ToShortDateString()
                + " " + fromDateTime.ToShortTimeString()
                + " to " + toDateTime.ToShortDateString()
                + " " + toDateTime.ToShortTimeString();
            }
        } //ends private void timeRangeButton_Click(...

        private void gridListViewButton_Click(object sender, EventArgs e)
        {   //switch between list and grid views
            if (MENULISTVIEW == gridListViewButton.Text)
                ActivateListView();
            else
            {
                this.Cursor = Cursors.WaitCursor;
                ActivateGridView();
                this.Cursor = Cursors.Default;
            }
        }

        /// <summary>
        /// Launches a form to display the names of filters lists available.
        /// Filter lists consist of drives, paths and/or folders to search or ignore during a search.
        /// </summary>
        private void FilterButton_Click(object sender, EventArgs e)
        {
            Application.Idle -= new System.EventHandler(OnIdle);
            using (SelectFilterList diskFileFilters = new SelectFilterList())
            {
                diskFileFilters.FormLocation = this.Location;
                diskFileFilters.ShowDialog();
                if (DialogResult.OK == diskFileFilters.DialogResult)
                {
                    ConfigInfo configInfo = new ConfigInfo();
                    selectedDisks = configInfo.FilterSelectedDisks(selectedDisks);
                    HandleDriveSelection();
                    msgLabel1.Text =
                    msgLabel2.Text = "";
                    FilterList filterList = configInfo.ActiveFilterList;
                    if (null != filterList)
                        filterLabel.Text = "Using filter list " + filterList.Name;
                    else
                        filterLabel.Text = "No filter list is being used";
                }
            }
            fileNameBox.Focus();
            Application.Idle += new System.EventHandler(OnIdle);
        } //ends private void FilterButton_Click(...
        #endregion Button processing

        private void DisplayFileFolderInfo(string columnName, int row)
        {   //display file, folder or disk volume info in the message area
            switch (columnName)
            {
                case GTFILENAME:
                case GTDIRECTORY:
                    break;
                case GTDISK:
                case GTVOLUMEID:
                    DisplayDriveInfo(gridView[GTDISK, row].Value.ToString());
                    return;
                default:
                    return;
            }
            string fileFullName = "";
            if (foldersOnlyDisplay)
            {
                fileFullName = Path.Combine(gridView[GTDISK, row].Value.ToString(),
                        gridView[GTDIRECTORY, row].Value.ToString());
                if (!Directory.Exists(fileFullName))
                {
                    msgLabel1.Text = "Directory not found";
                    msgLabel2.Text = null;
                    return;
                }
            }
            else
            {
                string fileName = gridView[GTFILENAME, row].Value.ToString();
                if ((String.IsNullOrEmpty(fileName.Trim()))
                || (GTDIRECTORY == columnName))
                {
                    fileFullName = Path.Combine(
                           gridView[GTDISK, row].Value.ToString(),
                           gridView[GTDIRECTORY, row].Value.ToString());
                    try
                    {
                        if (!Directory.Exists(fileFullName))
                        {
                            msgLabel1.Text = "Directory not found";
                            msgLabel2.Text = null;
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        msgLabel1.Text = "Access to directory is denied";
                        msgLabel2.Text = ex.Message;
                        return;
                    }
                }
                else
                {
                    fileFullName = Path.Combine(
                           Path.Combine(gridView[GTDISK, row].Value.ToString(),
                               gridView[GTDIRECTORY, row].Value.ToString()),
                           gridView[GTFILENAME, row].Value.ToString());
                    try
                    {
                        if (!File.Exists(fileFullName))
                        {
                            msgLabel1.Text = "File not found";
                            msgLabel2.Text = null;
                            return;
                        }
                    }
                    catch (Exception ex)
                    {
                        msgLabel1.Text = "Access to file is denied";
                        msgLabel2.Text = ex.Message;
                        return;
                    }
                }
            }

            try
            {
                FileInfo fileInfo = new FileInfo(fileFullName);
                if ((fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                    GetFolderStats(fileInfo);
                else
                    GetFileStats(fileInfo);
            }
            catch (Exception ex)
            { msgLabel1.Text = ex.Message; msgLabel2.Text = null; }
        } //ends private void DisplayFileFolderInfo(...

        private void FileFolderLaunch(string columnName, int row)
        {   //Launch a file, folder or disk volume
            switch (columnName)
            {
                case GTFILENAME:
                case GTDIRECTORY:
                    break;
                case GTDISK:
                case GTVOLUMEID:
                    {
                        string diskName = gridView[GTDISK, row].Value.ToString();
                        DisplayDriveInfo(diskName);
                        try
                        {
                            if (!PerformLaunch(diskName))
                                msgLabel1.Text = "Unable to launch " + diskName;
                        }
                        catch (Exception ex)
                        { msgLabel1.Text = ex.Message; msgLabel2.Text = null; }
                        return;
                    }
                default:
                    return;
            }
            string 
                fileName = "",
                fileFullName = "";
            if (!foldersOnlyDisplay)
                fileName = gridView[GTFILENAME, row].Value.ToString().Trim();
            if ((String.IsNullOrEmpty(fileName.Trim()))
            || (GTDIRECTORY == columnName))
            {
                fileFullName = Path.Combine(
                       gridView[GTDISK, row].Value.ToString(),
                       gridView[GTDIRECTORY, row].Value.ToString());
                try
                {
                    if (!Directory.Exists(fileFullName))
                    {
                        msgLabel1.Text = "Directory not found";
                        msgLabel2.Text = null;
                        return;
                    }
                }
                catch (Exception ex)
                {
                    msgLabel1.Text = "Access to directory is denied";
                    msgLabel2.Text = ex.Message;
                    return;
                }
            }
            else
            {
                fileFullName = Path.Combine(
                    Path.Combine(gridView[GTDISK, row].Value.ToString(),
                        gridView[GTDIRECTORY, row].Value.ToString()),
                        fileName);
                try
                {
                    if (!File.Exists(fileFullName))
                    {
                        msgLabel1.Text = "File not found";
                        msgLabel2.Text = null;
                        return;
                    }
                }
                catch (Exception ex)
                {
                    msgLabel1.Text = "Access to file is denied";
                    msgLabel2.Text = ex.Message;
                    return;
                }
            }
            try
            {
                FileInfo fileInfo = new FileInfo(fileFullName);
                if ((fileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                    GetFolderStats(fileInfo);
                else
                    GetFileStats(fileInfo);
                if(!PerformLaunch(fileInfo.FullName))
                    msgLabel1.Text = "Unable to launch " + fileInfo.FullName;
            }
            catch (Exception ex)
            { msgLabel1.Text = ex.Message; msgLabel2.Text = null; }
        } //ends private void FileFolderLaunch(...
        #region Menu processing

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {   //Menu->File->Save list
            //creates either a .CSV file or a .TXT file depending on which view is active
            using (SaveFileDialog savedlg = new SaveFileDialog())
            {
                savedlg.AddExtension = true;
                if (MENUGRIDVIEW == gridListViewButton.Text)
                {
                    savedlg.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*";
                    savedlg.Title = Application.ProductName + " save as text file";
                }
                else
                {
                    savedlg.Filter = "Comma value seperated files (*.csv)|*.csv|All files (*.*)|*.*";
                    savedlg.Title = Application.ProductName + " save as tab separated file";
                }
                if (DialogResult.OK == savedlg.ShowDialog())
                {
                    lock (syncobj)
                    {
                        string save_name = savedlg.FileName;
                        //if the menu shows 'Grid view', then the list view is visible
                        if (MENUGRIDVIEW == gridListViewButton.Text)
                        {   //save list view data as a text file
                            try
                            {
                                using (StreamWriter streamwriter = new StreamWriter(File.Create(save_name)))
                                {
                                    foreach (string str in listBox1.Items)
                                        streamwriter.WriteLine(str);
                                }
                                msgLabel2.Text = "Saved " + save_name;
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show(ex.Message, Application.ProductName,
                                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            }
                        }
                        else
                        {   //save grid view data as a tab seperated file
                            try
                            {
                                using (StreamWriter streamwriter = new StreamWriter(File.Create(save_name)))
                                {   //gather and write column titles
                                    StringBuilder columnTitles = new StringBuilder(256);
                                    foreach (DataColumn dc in table.Columns)
                                        AddTitle(ref columnTitles, dc.ColumnName);
                                    streamwriter.WriteLine(columnTitles.ToString());
                                    //write all data rows
                                    foreach (DataRow row in table.Rows)
                                    {
                                        columnTitles.Length = 0;
                                        foreach (DataColumn dc in table.Columns)
                                        {
                                            if (String.IsNullOrEmpty(row[dc.ColumnName].ToString()))
                                            {
                                                if (GTFILENAME == dc.ColumnName)
                                                    AddTitle(ref columnTitles, "n/a");
                                                else if (GTSIZE == dc.ColumnName)
                                                    AddTitle(ref columnTitles, "0");
                                            }
                                            else
                                                AddTitle(ref columnTitles, row[dc.ColumnName].ToString());
                                        }
                                        streamwriter.WriteLine(columnTitles.ToString());
                                    }
                                }
                                msgLabel2.Text = "Saved " + save_name;
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show(ex.Message, Application.ProductName,
                                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                            }
                        }
                    } //ends lock (syncobj)
                } //ends if (DialogResult.OK...
            }
        } //ends private void saveToolStripMenuItem_Click(...

        private void AddTitle(ref StringBuilder sb, string str)
        {   //creates a tab separated string of words for CSV files
            if (str.IndexOf('\"') > -1)     //handle embedded double quotes
            {
                StringBuilder newSB = new StringBuilder(256);
                int index = str.IndexOf('\"');
                newSB.Append(str.Substring(0, index++));
                newSB.Append("\"\"");
                while (index < str.Length)
                {
                    if ("\"" == str.Substring(index, 1))
                        newSB.Append("\"\"");
                    else
                        newSB.Append(str.Substring(index, 1));
                    ++index;
                }
                str = newSB.ToString();
            }
            char[] reservedChar = new char[] { ',', '\n', '~' }; //handle embedded commas and new line characters
            if(str.IndexOfAny(reservedChar) > 0)
                str = "\"" + str + "\"";
            if (sb.Length > 1)
                sb.Append("\t" + str);
            else
                sb.Append(str);
        } //ends private void AddTitle(...

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {   // Menu->File->Exit
            Application.Exit();
        }

        private void fontToolStripMenuItem_Click(object sender, EventArgs e)
        {   // Menu->Options->Font
            //select a different font
            using (FontDialog fontDialog = new FontDialog())
            {
                if (fontDialog.ShowDialog() == DialogResult.OK)
                {
                    ConfigInfo configInfo = new ConfigInfo();
                    configInfo.UseFont =
                    this.Font = fontDialog.Font;
                }
            }
        }

        private void showUnauthorizedFoldersToolStripMenuItem_Click(object sender, EventArgs e)
        {   //Menu->Options->Show unauthorized folders
            fileCountLabel.Text =
            msgLabel1.Text = "";
            ConfigInfo configInfo = new ConfigInfo();
            showUnauthorizedFoldersToolStripMenuItem.Checked = 
            configInfo.ShowUnauthorizedFolders = !showUnauthorizedFoldersToolStripMenuItem.Checked;
            if (configInfo.ShowUnauthorizedFolders)
                msgLabel2.Text = "Showing unauthorized folder/file accesses";
            else
                msgLabel2.Text = "Not showing unauthorized folder/file accesses";
        }

        private void showReparsePointsToolStripMenuItem_Click(object sender, EventArgs e)
        {   //Menu->Options->Show reparse points
            fileCountLabel.Text =
            msgLabel1.Text = "";
            ConfigInfo configInfo = new ConfigInfo();
            showReparsePointsToolStripMenuItem.Checked =
            configInfo.ShowReparsePoints = !showReparsePointsToolStripMenuItem.Checked;
            if (configInfo.ShowReparsePoints)
                msgLabel2.Text = "Showing reparse points";
            else
                msgLabel2.Text = "Not showing reparse points";
        }

        private void showHiddenFoldersfilesToolStripMenuItem_Click(object sender, EventArgs e)
        {   //Menu->Options->Show unauthorized folders
            fileCountLabel.Text =
            msgLabel1.Text = "";
            ConfigInfo configInfo = new ConfigInfo();
            showHiddenFoldersfilesToolStripMenuItem.Checked =
            configInfo.ShowHiddenFoldersFiles = !showHiddenFoldersfilesToolStripMenuItem.Checked;
            if (configInfo.ShowHiddenFoldersFiles)
                msgLabel2.Text = "Showing hidden folders and files";
            else
                msgLabel2.Text = "Not showing hidden folders and files";
        }

        private void autoScrollingToolStripMenuItem_Click(object sender, EventArgs e)
        {   //Menu->Options->Show unauthorized folders
            fileCountLabel.Text =
            msgLabel1.Text = "";
            ConfigInfo configInfo = new ConfigInfo();
            autoScrollMode = 
            autoScrollingToolStripMenuItem.Checked =
            configInfo.AutoScrolling = !autoScrollingToolStripMenuItem.Checked;
            if (configInfo.AutoScrolling)
                msgLabel2.Text = "Auto scrolling on";
            else
                msgLabel2.Text = "Auto scrolling off";
        }

        private void helpToolStripMenuItem1_Click(object sender, EventArgs e)
        {   // Menu->Help->Help
            HelpForm helpForm = new HelpForm();
            //put the new form to the right and down unless the caption would not be visible
            System.Drawing.Rectangle formBounds = this.Bounds;
            int x = formBounds.Width / 2 + formBounds.X;
            int y = formBounds.Y + formBounds.Height / 10;
            Screen scrn = Screen.FromControl(this);
            if (x > scrn.WorkingArea.Right)
                x = formBounds.Right - formBounds.Width / 4;
            if (y > scrn.WorkingArea.Bottom)
                y = formBounds.Bottom - formBounds.Height / 10;
            System.Drawing.Point helpLocation = new System.Drawing.Point(x, y);
            helpForm.FormLocation = helpLocation;
            helpForm.FormHeight = formBounds.Height - formBounds.Height / 10;
            helpForm.HelpFor = HelpForm.HelpForForm.MainForm;
            helpForm.Show();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {   // Menu->Help->About
            //display the About form
            using (AboutBox aboutDialog = new AboutBox())
            {
                aboutDialog.FormLocation = this.Location;
                aboutDialog.ShowDialog();
            }
        }

        private void ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (sender is System.Windows.Forms.ToolStripMenuItem)
            {
                System.Windows.Forms.ToolStripMenuItem menuItem
                   = (System.Windows.Forms.ToolStripMenuItem)sender;
                gridView.Columns[menuItem.Text].Visible =
                menuItem.Checked = !menuItem.Checked;
                usingView.UpdateVisible(menuItem.Text, menuItem.Checked);
            }
        } //ends private void ToolStripMenuItem_Click(...

        private void removeSavedOptionsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ConfigInfo configInfo = new ConfigInfo();   //access configuration information
            configInfo.RemoveIsolatedStorage();
            fileCountLabel.Text =
            msgLabel1.Text = "";
            msgLabel2.Text = "Saved options file was deleted";
        }

        private void scanningViaEnumeratorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            fileCountLabel.Text =
            msgLabel1.Text = "";
            ConfigInfo configInfo = new ConfigInfo();
            scanningViaEnumeratorToolStripMenuItem.Checked =
            configInfo.ScanningViaEnumerator = !scanningViaEnumeratorToolStripMenuItem.Checked;
            if (configInfo.ScanningViaEnumerator)
                msgLabel2.Text = "Scanning via enumerator technique";
            else
                msgLabel2.Text = "Scanning using FileInfo/DirInfo technique";
        }

        private void sequentialSearchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            sequentialSearchToolStripMenuItem.Checked =
            performSequentialSearch = !sequentialSearchToolStripMenuItem.Checked;
            ConfigInfo configInfo = new ConfigInfo();
            selectedDisks = configInfo.FilterSelectedDisks(selectedDisks);
            HandleDriveSelection();
            WaitForResolvedCompletion();
            if (0 == resolvedDrives.Count)
                msgLabel2.Text = "No disks were selected";
        }

        #endregion Menu processing

        #region Grid cell clicked
        private void CellContextClicked(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
        {
            List<string> columnTitles = new List<string>(table.Columns.Count);   //array of string containing column names
            foreach (DataColumn dc in table.Columns)                        //populate the array
                columnTitles.Add(dc.ColumnName.ToString());
            cellRowClicked = e.RowIndex;
            cellColumnName = columnTitles[e.ColumnIndex].ToString();
        }

        /// <summary>
        /// Grid was right clicked and content menu selected
        /// </summary>
        private void displayInfoStripMenuItem_Click(object sender, EventArgs e)
        {
            if (cellRowClicked != -1)
                DisplayFileFolderInfo(cellColumnName, cellRowClicked);
            cellRowClicked = -1;
        }

        private void launchFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (cellRowClicked != -1)
                FileFolderLaunch(GTFILENAME, cellRowClicked);
            cellRowClicked = -1;
        }

        private void launchFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (cellRowClicked != -1)
                FileFolderLaunch(GTDIRECTORY, cellRowClicked);
            cellRowClicked = -1;
        }

        private void launchDiskToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (cellRowClicked != -1)
                FileFolderLaunch(GTDISK, cellRowClicked);
            cellRowClicked = -1;
        }

        private void KeyUpEvent(object sender, KeyEventArgs e)
        {   //The Delete key was pressed
            if (e.KeyCode == Keys.Delete)
            {
                if (MENULISTVIEW == gridListViewButton.Text)
                {   //in grid view
                    UserRequestedDelete();
                    e.Handled = true;
                }
                else
                    e.Handled = false;
            }
        }
        #endregion Grid cell clicked
    }
}

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