//#define useRecurseFoldersOnly
// Defining useRecurseFoldersOnly will cause the RecurseFoldersOnly routine to be called when
// searching for folder names only. This did not prove to be a significant time saver.
// I elected to run with enumerators on networked disks as the default because enumerators were always fastest.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO; //for DirectoryInfo and FileInfo
using System.Threading;
using System.Windows.Forms;
namespace FileFind
{
/// <summary>
/// One DiskScan thread is started for each physical disk drive.
/// DiskScan performs recursive scans of directories looking for file and/or directory matches.
/// Information is passed back to the main display via delegates provided at construction.
/// All errors and informational messages sent via SendMsg are preceded by a single
/// space so the information may be sorted out from the files and directories that
/// are found.
/// </summary>
class DiskScan
{
#region Local constants and variables
// Reference to main form used to make syncronous user interface calls:
private ContainerControl m_form;
private Delegate DisplayMsg = null;
private Delegate SendDirInfo = null;
private Delegate SendFileInfo = null;
private ManualResetEvent pleaseTerminate;
private Delegate ThreadTerminating = null;
private String disksToScan = null; //Set before issuing RUN
//Each entry contains a drive to scan; for example: "C:\"
//following fields identify which folders and files to search for
private String filesToFind = null;
private String[] fileMasks = null;
private List<Wildcard> wildCards = null; //contains: Wildcard objects
bool directoriesOnly = true;
private enum searchingForType
{
searchingForTypeFile,
searchingForTypeFolder,
searchingForTypeBoth
};
private searchingForType searchingFor;
private ConfigInfo configInfo;
bool showingHiddenFolders; //local copy of configInfo.ShowHiddenFoldersFiles
bool scanViaEnumerator; //set true if configInfo.ScanningViaEnumerator is true
//or DriveType.Network == driveInfo.DriveType
private DateTimeInfo usingDateTimeRange;
private DateTime
fromFileTime,
toFileTime;
private string volumeId = null; //currently processing this volume label
private bool scanningSpecificPaths; //Set to true if a rule specified a specific path
//is to be searched. True will prevent testing for
//exclusions on lower level directories.
FilterRules filterRules = null;
#endregion Local constants and variables
#region Constructors
public DiskScan(
ContainerControl form, //the user interface
Delegate DsplyMsg, //send text messages
Delegate SendDir, //send directory information
Delegate SendFile, //send file information
ManualResetEvent terminateRequest, //receive a termination request
Delegate ThreadStopping, //send an end of thread message
DateTimeInfo selectedDateTime //provides date/time range information
)
{
m_form = form;
DisplayMsg = DsplyMsg;
SendDirInfo = SendDir;
SendFileInfo = SendFile;
pleaseTerminate = terminateRequest;
ThreadTerminating = ThreadStopping;
usingDateTimeRange = selectedDateTime;
}
#endregion Constructors
#region Properties
public String SearchingDrives
{
get { return disksToScan; }
set { disksToScan = value; }
}
public String IFindFiles
{ set { filesToFind = value; } }
public bool SearchForFiles
{ set { searchingFor = DiskScan.searchingForType.searchingForTypeFile; } }
public bool SearchForFolders
{ set { searchingFor = DiskScan.searchingForType.searchingForTypeFolder; } }
public bool SearchForBoth
{ set { searchingFor = DiskScan.searchingForType.searchingForTypeBoth; } }
#endregion Properties
/// <summary>
/// Insure preconditions are met before searching the assigned disk(s)
/// Catch exceptions to insure the ThreadTerminating message is sent
/// </summary>
public void Run()
{
try
{
if (DateTimeInfo.DateTimeRangeSearch.DateTimeRangeOff != usingDateTimeRange.IDateTimeRange)
{
fromFileTime = usingDateTimeRange.IFromDateTime;
toFileTime = usingDateTimeRange.IToDateTime;
}
configInfo = new ConfigInfo();
if ((DiskScan.searchingForType.searchingForTypeFile == searchingFor)
|| (DiskScan.searchingForType.searchingForTypeBoth == searchingFor))
directoriesOnly = false;
showingHiddenFolders = configInfo.ShowHiddenFoldersFiles; //create local copy
if (!String.IsNullOrEmpty(filesToFind))
{ //build regular expressions for all file masks
fileMasks = filesToFind.Split(new char[1] { Path.PathSeparator });
wildCards = new List<Wildcard>(fileMasks.Length);
foreach (string mask in fileMasks) //set up masks as regular expressions
wildCards.Add(new Wildcard(mask,
System.Text.RegularExpressions.RegexOptions.IgnoreCase));
filterRules = ConfigInfo.GetFilterRules; //get a local reference to the current filter set rules
ArrayList disksToScan = GetScanAreas();
foreach (string disk in disksToScan)
try
{
DriveInfo driveInfo = new DriveInfo(disk);
if (driveInfo.IsReady)
{
scanningSpecificPaths = false;
if (disk.Length > 3)
scanningSpecificPaths = true;
volumeId = driveInfo.VolumeLabel;
if (configInfo.ScanningViaEnumerator
|| (DriveType.Network == driveInfo.DriveType))
scanViaEnumerator = true;
else
scanViaEnumerator = false;
#if useRecurseFoldersOnly
if (DiskScan.searchingForType.searchingForTypeFolder == searchingFor)
RecurseFoldersOnly(disk);
else
#endif
RecurseFileDir(disk);
}
}
catch (ThreadAbortException)
{ //stop the abort and fall through to the thread termination notification
Thread.ResetAbort();
}
catch (ObjectDisposedException)
{ return; }
catch (UnauthorizedAccessException)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(" Unauthorized Access: " + disk); }
continue;
}
catch (InvalidOperationException ex)
{
MessageBox.Show("InvalidOperationException " + Thread.CurrentThread.Name
+ "\n" + ex.Message
+ "\n" + ex.Source,
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
break;
}
catch (Exception ex)
{
MessageBox.Show("Error scanning " + Thread.CurrentThread.Name
//+ "\nType0: " + ex.GetType().ToString()
+ "\n" + ex.Message
+ "\n" + ex.Source,
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
continue;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error in DiskScan Run " + Thread.CurrentThread.Name
//+ "\nType0: " + ex.GetType().ToString()
+ "\n" + ex.Message
+ "\n" + ex.Source,
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
try { m_form.Invoke(ThreadTerminating, new Object[] { Thread.CurrentThread.Name }); }
catch {}
} //ends public void Run()
/// <summary>
/// Determine if the entire disk or only specific paths are to be searched.
/// </summary>
/// <returns>drive or path names to be searched</returns>
private ArrayList GetScanAreas()
{//search all drives assigned to this thread
ArrayList scan = new ArrayList();
string[] diskToScan = disksToScan.Split(new char[1] { ';' });
foreach (string drive in diskToScan)
{
ArrayList scanPaths = filterRules.FetchPaths(drive, FilterRules.Functions.Include);
if (0 == scanPaths.Count)
scan.Add(drive); //search entire drive
else
scan.AddRange(scanPaths); //search only selected paths
}
return scan;
} //ends private ArrayList GetScanAreas()
void SendMsg(string msg)
{ m_form.Invoke(DisplayMsg, new Object[] { msg }); }
void SendingDirInfo(DirectoryInfo directoryInfo)
{
int pause = (int)m_form.Invoke(SendDirInfo, directoryInfo, volumeId);
if (0 != pause)
Thread.Sleep(pause);
}
void SendingFileInfo(FileInfo fileInfo)
{
int pause = (int)m_form.Invoke(SendFileInfo, fileInfo, volumeId);
if (0 != pause)
Thread.Sleep(pause);
}
/// <summary>
/// Recurse through all folders on a disk to insure pattern matches
/// for folders and files are found.
/// Any changes made to this module must also be made to RecurseEnumerator(...)
/// </summary>
/// <param name="msPathName">
/// The first entry to this routine is the top level disk name, ie C:\ D:\ etc.
/// There after, it contains the recursive folder names on the disk.
/// </param>
private void RecurseFileDir(string msPathName)
{ //called recursively to search for path and file names
if (pleaseTerminate.WaitOne(0, true))
return;
int pleaseTerminateCounter = 0; //number of files to process before checking pleaseTerminate
DirectoryInfo processDirectory = new DirectoryInfo(msPathName);
if ((!processDirectory.Exists)
|| ((processDirectory.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint))
return;
if ((processDirectory.Attributes & FileAttributes.System) != FileAttributes.System)
if (((processDirectory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
&& !showingHiddenFolders)
return;
if (scanViaEnumerator)
{
using (FileSystemEnumerator fse = new FileSystemEnumerator(msPathName, directoriesOnly))
{
IEnumerator<FileSystemInfo> ien = fse.Matches().GetEnumerator();
foreach (FileSystemInfo fileSystemInfo in fse.Matches())
{
if (++pleaseTerminateCounter > 5)
{ //check only occasionally to avoid overhead
pleaseTerminateCounter = 0;
if (pleaseTerminate.WaitOne(0, true))
return;
}
ProcessFileSystemInfo(fileSystemInfo, ref msPathName);
}
}
}
else
{
FileSystemInfo[] fileSystemInfos = processDirectory.GetFileSystemInfos();
foreach (FileSystemInfo fileSystemInfo in fileSystemInfos)
{
if (++pleaseTerminateCounter > 5)
{ //check only occasionally to avoid overhead
pleaseTerminateCounter = 0;
if (pleaseTerminate.WaitOne(0, true))
return;
}
ProcessFileSystemInfo(fileSystemInfo, ref msPathName);
}
}
} //ends private void RecurseFileDir(...
#if useRecurseFoldersOnly
/// <summary>
/// Recurse through all folders on a disk to insure pattern matches for all folders are found.
/// </summary>
/// <param name="msPathName">
/// The first entry to this routine is the top level disk name, ie C:\ D:\ etc.
/// There after, it contains the recursive folder names on the disk.
/// </param>
private void RecurseFoldersOnly(string msPathName)
{ //called recursively to search for path and file names
if (pleaseTerminate.WaitOne(0, true))
return;
int pleaseTerminateCounter = 0;
DirectoryInfo processDirectory = new DirectoryInfo(msPathName);
if ((!processDirectory.Exists)
|| ((processDirectory.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint))
return;
if ((processDirectory.Attributes & FileAttributes.System) != FileAttributes.System)
if (!showingHiddenFolders
&& ((processDirectory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden))
return;
DirectoryInfo[] directoryInfoInfos = processDirectory.GetDirectories("*");
foreach (DirectoryInfo directoryInfo in directoryInfoInfos)
{
try
{
if (++pleaseTerminateCounter > 5)
{
pleaseTerminateCounter = 0;
if (pleaseTerminate.WaitOne(0, true))
return;
}
if (!filterRules.SearchThisPath(directoryInfo))
continue; //do not process
try
{
msPathName = directoryInfo.FullName;
bool wildCardCheck = false;
switch (usingDateTimeRange.IDateTimeRange)
{
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeOff:
wildCardCheck = true;
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByCreate:
{
DateTime dateTime = directoryInfo.CreationTime;
if ((dateTime >= fromFileTime)
&& (dateTime <= toFileTime))
wildCardCheck = true;
}
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByModified:
{
DateTime dateTime = directoryInfo.LastWriteTime;
if ((dateTime >= fromFileTime)
&& (dateTime <= toFileTime))
wildCardCheck = true;
}
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByLastAccessed:
{
DateTime dateTime = directoryInfo.LastAccessTime;
if ((dateTime >= fromFileTime)
&& (dateTime <= toFileTime))
wildCardCheck = true;
}
break;
}
if (wildCardCheck)
foreach (Wildcard wildCard in wildCards)
{
if (wildCard.IsMatch(directoryInfo.Name))
{
SendingDirInfo(directoryInfo);
break; //do not check any further wildcard masks
}
} //ends foreach (Wildcard wildCard...
}
catch (PathTooLongException)
{ SendMsg(" Path/File too long; ignoring " + msPathName); continue; }
catch (UnauthorizedAccessException ex)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(ex.Message); continue; }
}
}
catch (UnauthorizedAccessException)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(" Unauthorized Access: " + msPathName); }
continue;
}
catch (ThreadAbortException)
{ return; }
catch (ObjectDisposedException)
{ throw; }
catch (IOException)
{ throw; }
catch (InvalidOperationException)
{ throw; }
catch (Exception ex)
{
MessageBox.Show("Error scanning " + Thread.CurrentThread.Name
//+ "\nType3: " + ex.GetType().ToString()
+ "\n" + ex.Message
+ "\n" + ex.Source,
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
continue;
}
if (Directory.Exists(msPathName))
{
try { RecurseFoldersOnly(msPathName); }
catch (UnauthorizedAccessException)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(" Unauthorized Access: " + msPathName); }
continue;
}
}
}
} //ends private void RecurseFoldersOnly(...
#endif
private void ProcessFileSystemInfo(FileSystemInfo fileSystemInfo, ref string msPathName)
{
try
{
if (fileSystemInfo is DirectoryInfo)
{ //directories are always searched
DirectoryInfo directoryInfo = fileSystemInfo as DirectoryInfo;
if (scanningSpecificPaths)
{
if (filterRules.SpecificallyExcludedPath(directoryInfo))
return;
}
else if (!filterRules.SearchThisPath(directoryInfo))
return; //do not process
msPathName = directoryInfo.FullName;
if ((DiskScan.searchingForType.searchingForTypeFolder == searchingFor)
|| (DiskScan.searchingForType.searchingForTypeBoth == searchingFor))
try
{
bool wildCardCheck = false;
switch (usingDateTimeRange.IDateTimeRange)
{
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeOff:
wildCardCheck = true;
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByCreate:
{
DateTime dateTime = directoryInfo.CreationTime;
if ((dateTime >= fromFileTime)
&& (dateTime <= toFileTime))
wildCardCheck = true;
}
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByModified:
{
DateTime dateTime = directoryInfo.LastWriteTime;
if ((dateTime >= fromFileTime)
&& (dateTime <= toFileTime))
wildCardCheck = true;
}
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByLastAccessed:
{
DateTime dateTime = directoryInfo.LastAccessTime;
if ((dateTime >= fromFileTime)
&& (dateTime <= toFileTime))
wildCardCheck = true;
}
break;
}
if (wildCardCheck)
foreach (Wildcard wildCard in wildCards)
{
if (wildCard.IsMatch(directoryInfo.Name))
{
SendingDirInfo(directoryInfo);
break; //do not check any further wildcard masks
}
} //ends foreach (Wildcard wildCard...
}
catch (PathTooLongException)
{
SendMsg(" Path/File too long; ignoring " + msPathName);
return;
}
catch (UnauthorizedAccessException ex)
{
if (configInfo.ShowUnauthorizedFolders)
{
SendMsg(ex.Message);
return;
}
}
catch (InvalidOperationException)
{ throw; }
if (Directory.Exists(msPathName))
{
try { RecurseFileDir(msPathName); }
catch (UnauthorizedAccessException)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(" Unauthorized Access: " + msPathName); }
return;
}
}
} //ends if (fileSystemInfo is DirectoryInfo)
else if ((fileSystemInfo is FileInfo)
&& ((DiskScan.searchingForType.searchingForTypeFile == searchingFor)
|| (DiskScan.searchingForType.searchingForTypeBoth == searchingFor)))
{
FileInfo fileInfo = fileSystemInfo as FileInfo;
if (((fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
&& !showingHiddenFolders)
return;
switch (usingDateTimeRange.IDateTimeRange)
{
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeOff:
break;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByCreate:
if ((fileInfo.CreationTime >= fromFileTime)
&& (fileInfo.CreationTime <= toFileTime))
break;
else
return;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByModified:
if ((fileInfo.LastWriteTime >= fromFileTime)
&& (fileInfo.LastWriteTime <= toFileTime))
break;
else
return;
case DateTimeInfo.DateTimeRangeSearch.DateTimeRangeByLastAccessed:
if ((fileInfo.LastAccessTime >= fromFileTime)
&& (fileInfo.LastAccessTime <= toFileTime))
break;
else
return;
}
foreach (Wildcard wildCard in wildCards)
{
try
{
if (wildCard.IsMatch(fileInfo.Name))
try
{
if (fileInfo.FullName != "." && fileInfo.FullName != "..")
{
SendingFileInfo(fileInfo);
break; //do not check any further wildcard masks
}
}
catch (UnauthorizedAccessException)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(" Unauthorized Access: " + msPathName); }
continue;
}
}
catch (PathTooLongException)
{ SendMsg(" File/Path too long; ignoring " + msPathName); continue; }
} //ends foreach(Wildcard...
} //ends if ((fileSystemInfo is FileInfo...
}
catch (UnauthorizedAccessException)
{
if (configInfo.ShowUnauthorizedFolders)
{ SendMsg(" Unauthorized Access: " + msPathName); }
return;
}
catch (ThreadAbortException)
{ return; }
catch (ObjectDisposedException)
{ throw; }
catch (IOException)
{ throw; }
catch (InvalidOperationException)
{ throw; }
catch (Exception ex)
{
MessageBox.Show("Error scanning " + Thread.CurrentThread.Name
//+ "\nType1: " + ex.GetType().ToString()
+ "\n" + ex.Message
+ "\n" + ex.Source,
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
} //ends private void ProcessFileSystemInfo(FileSystemInfo fileSystemInfo)
}
}