//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "FileFindThread.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(&UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall TFileFindThread::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------
/*
Default constructor. Call the base class with a boolean to indicate
if the thread should be created in suspended state. This will allow
the client to set the attributes of the thread before it starts to
run.
*/
__fastcall TFileFindThread::TFileFindThread(void) : TThread(true)
{
FFileSpecs = 0;
FFileList = 0;
FCallbackProc = 0;
FCompletedProc = 0;
FID = -1;
FIncludeSubdirectories = false;
OnTerminate = OnThreadTerminate; // this is called when the thread finishes
FreeOnTerminate = true; // let the thread "free" itself when done
}
//---------------------------------------------------------------------------
__fastcall TFileFindThread::~TFileFindThread(void)
{
delete FFileSpecs;
}
/*
Function that is called when the thread has finished (or been terminated)
*/
void __fastcall TFileFindThread::OnThreadTerminate(TObject *Sender)
{
if (FCompletedProc)
FCompletedProc(FID);
}
/*
This starts the thread.
*/
void __fastcall TFileFindThread::Execute()
{
FindFiles(FDirectory, *FFileSpecs, *FFileList, FIncludeSubdirectories);
}
//---------------------------------------------------------------------------
/*
Proxy for the callback. It must be Synchronized, and therefore, has
a specific signature.
*/
void __fastcall TFileFindThread::CallbackProxy(void)
{
if (FCallbackProc)
FCallbackProc(FFInfo, FID);
}
/*
Proxy for the callback. It must be Synchronized, and therefore, has
a specific signature. (Actually, if called from the OnTerminate handler,
it doesn't need to be Synchronized.)
*/
void __fastcall TFileFindThread::CompletedProxy(void)
{
if (FCompletedProc)
FCompletedProc(FID);
}
/*
Retrieves the names of all of the files in 'Directory' that match the
file specification 'FileSpec' and adds them to 'List'.
*/
int TFileFindThread::CatalogFiles(const AnsiString& Directory, const AnsiString& FileSpec, TStringList& List)
{
TSearchRec finfo;
int count = 0;
AnsiString dir = IncludeTrailingPathDelimiter(Directory);
// Prime the loop (it's like an iterator paradigm)
int done = FindFirst(dir + FileSpec, faAnyFile, finfo);
while ((done == 0) && (!Terminated))
{
if ((finfo.Name != ".") && (finfo.Name != ".."))
{
// Convert file date format to a "better" format
FFInfo.DateModified = FileDateToDateTime(finfo.Time);
FFInfo.Name = finfo.Name;
FFInfo.Extension = ExtractFileExt(finfo.Name);
FFInfo.Path = Directory;
FFInfo.Size = finfo.Size;
// If it's a directory (folder)
if ((finfo.Attr & faDirectory) != 0)
{
FFInfo.IsFolder = true;
FFInfo.Extension = "Folder";
List.Add(dir + finfo.Name);
}
else
{
FFInfo.IsFolder = false;
List.Add(dir + finfo.Name);
}
count++;
// Callback client with information
if (FCallbackProc)
Synchronize(&CallbackProxy);
}
done = FindNext(finfo);
Application->ProcessMessages();
}
FindClose(finfo);
return count;
}
/*
This function recursively traverses through a file system looking for
specific files.
Directory - The directory to start in (e.g. D:\Data)
FileSpecs - A list of file specifications (e.g. *.txt, *.ini, abc???.*)
List - Found filenames will be added to this container.
IncludeSudirectories - Determines whether or not to recurse into subdirectories.
*/
int TFileFindThread::FindFiles(const AnsiString& Directory, TStringList& FileSpecs, TStringList& List, bool IncludeSubdirectories)
{
TSearchRec finfo;
AnsiString filespec;
AnsiString dir = IncludeTrailingPathDelimiter(Directory);
AnsiString dirspec = dir + "*.*";
// Get all files for each spec
int count = 0;
for (int i = 0; i < FileSpecs.Count; i++)
{
// Get the next filespec
filespec = FileSpecs[i];
// Find matching files in this directory
count = count + CatalogFiles(dir, filespec, List);
}
// Always search for *.* when looking for directories
if (IncludeSubdirectories)
{
int done = FindFirst(dirspec, faAnyFile, finfo);
while ((done == 0) && (!Terminated))
{
// Is this a directory? If so, recurse into it
if (((finfo.Attr & faDirectory) != 0) && (finfo.Name != ".") && (finfo.Name != ".."))
{
AnsiString subdir = Format("%s%s", ARRAYOFCONST((dir, finfo.Name)));
count += FindFiles(subdir, FileSpecs, List, IncludeSubdirectories);
}
done = FindNext(finfo);
Application->ProcessMessages();
}
FindClose(finfo);
}
return count;
}
void TFileFindThread::SetID(long ID)
{
FID = ID;
}
void TFileFindThread::SetFileList(TStringList* list)
{
FFileList = list;
}
void TFileFindThread::SetFileSpecs(TStringList* filespecs)
{
FFileSpecs = new TStringList();
FFileSpecs->Assign(filespecs);
}
void TFileFindThread::SetDirectory(const AnsiString& directory)
{
FDirectory = directory;
}
void TFileFindThread::SetCallbackProc(CALLBACKPROC callback)
{
FCallbackProc = callback;
}
void TFileFindThread::SetCompletedProc(COMPLETEDPROC callback)
{
FCompletedProc = callback;
}
void TFileFindThread::SetIncludeSubdirectories(bool Include)
{
FIncludeSubdirectories = Include;
}