|  Use FindFirst and FindNext to read a directoryby Curtis KrauskopfQ:  How can I use FindFirst and FindNext 
                          to read a directory listing? 
                         A:  Here's an example 
                          program: 
                          findfirst_example.zip (6K)
  
                         
                           
                            | 
#include <vcl.h>
#pragma hdrstop
#include "findfirst_form.h"
#include "dir.h"
//---------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  int iAttributes = 0;
  iAttributes |= faReadOnly * CheckBox1->Checked;
  iAttributes |= faHidden * CheckBox2->Checked;
  iAttributes |= faSysFile * CheckBox3->Checked;
  iAttributes |= faVolumeID * CheckBox4->Checked;
  iAttributes |= faDirectory * CheckBox5->Checked;
  iAttributes |= faArchive * CheckBox6->Checked;
  iAttributes |= faAnyFile * CheckBox7->Checked;
  // Reset the grid, display a default failure message (which will
  // be overwritten if a file is found)
  StringGrid1->RowCount = 2;
  StringGrid1->FixedRows = 1;
  StringGrid1->Rows[0]->CommaText = "Filename,Size,Attributes";
  StringGrid1->Cells[0][1] = "No Files Found";
  StringGrid1->Cells[1][1] = "";
  StringGrid1->Cells[2][1] = "";
  StringGrid1->Visible = true;
  displayFiles(Edit1->Text, iAttributes, 1);
  if (StringGrid1->RowCount > 2) StringGrid1->RowCount--;
}
//---------------------------------------------------------------------
void TForm1::displayFiles(AnsiString path, 
                          int iAttributes, 
                          int nestingLevel)
{
  TSearchRec sr;
  // Creating the nesting prefix for displaying with each file name
  AnsiString nesting = "";
  for (int i = 0; i < nestingLevel; i++)
    nesting += "  ";
  if (FindFirst(path, iAttributes, sr) == 0)
  {
    do
    {
      {
        StringGrid1->Cells[0][StringGrid1->RowCount-1] = nesting + sr.Name;
        StringGrid1->Cells[1][StringGrid1->RowCount-1] = IntToStr(sr.Size);
        StringGrid1->Cells[2][StringGrid1->RowCount-1] = 
             "0x" + IntToHex(sr.Attr & faAnyFile, 2);
        StringGrid1->RowCount = StringGrid1->RowCount + 1;
        if ((sr.Attr & faDirectory) && DrillDirectories->Checked) {
          // Do not drill into "." or ".."
          if (sr.Name != "." && sr.Name != "..") {
            AnsiString drillPath = appendDirectory(path, sr.Name);
            displayFiles(drillPath, iAttributes, (nestingLevel+1));
          }
        }
      }
    } while (FindNext(sr) == 0);
    FindClose(sr);
  }
  // If we are supposed to drill into directories but the faDirectory flag
  // was not set, that means findFirst skipped all of the directories.  Do
  // the search again, this time only looking for directories so that we
  // can drill into them.
  //
  if (DrillDirectories->Checked && ((faDirectory & iAttributes) == 0)) {
    // Use driveAndPath() to Create a path that does not contain a
    // filename or extension so that directories can be drilled into
    if (FindFirst(driveAndPath(path) + "*",
                  iAttributes | faDirectory,
                  sr) == 0)
    {
      do {
        if (sr.Attr & faDirectory) {
          if (sr.Name != "." && sr.Name != "..") {
            // When we find a directory that can be drilled into,
            //  recurse into that directory so that the matching
            //  files can be displayed.
            displayFiles(appendDirectory(path, sr.Name), 
                         iAttributes, 
                         nestingLevel+1);
          }
        }
      } while (FindNext(sr) == 0);
    }
    FindClose(sr);
  }
}
// Append the newDir directory to the path, retaining any 
// filename that might already be on the path.
//
AnsiString TForm1::appendDirectory(AnsiString path, AnsiString newDir)
{
  char drillIntoPath[MAXPATH];
  char drive[MAXDRIVE];
  char dir[MAXDIR];
  char file[MAXFILE];
  char ext[MAXEXT];
  fnsplit(path.c_str(),drive,dir,file,ext);
  strcat(dir, newDir.c_str());
  fnmerge(drillIntoPath,drive,dir,file,ext);
  return(AnsiString(drillIntoPath));
}
// Given a fully qualified filename, return just the drive and
// path.
//
AnsiString TForm1::driveAndPath(AnsiString path)
{
  char drillIntoPath[MAXPATH];
  char drive[MAXDRIVE];
  char dir[MAXDIR];
  char file[MAXFILE];
  char ext[MAXEXT];
  fnsplit(path.c_str(),drive,dir,file,ext);
  fnmerge(drillIntoPath,drive,dir,0,0);
  return(AnsiString(drillIntoPath));
}
 |  When this program is executed, it first prompts the 
                          user for a directory (with optional wildcards). The 
                          user can also check (tick) any of the optional FindFirst() 
                          flags:  
                          faReadOnlyfaHiddenfaSysFilefaVolumeIDfaDirectoryfaArchivefaAnyFile In addition, the user can also optionally choose to 
                          drill into subdirectories.  
 When the user clicks on the Search button, the directory 
                          listing is displayed in a TStringGrid: 
                          
 Another example of using this sample program is by 
                          checking (ticking) the faDirectory 
                          checkbox. The same directory listing appears like this: 
                          
 Notice that in this example, the current directory 
                          (".") and the parent directory ("..") 
                          are shown in the list. 
                         The Borland C++ Builder 6 documentation for FindFirst() 
                          says: 
                          
                          Searches for the first instance of a file name with 
                            a given set of attributes in a specified directory. I've found that this isn't exactly true. For example, 
                          in both of the above examples, files with a 0x20 
                          attribute are listed even though the faArchive 
                          bit was not set. Also, if Borland's documentation was 
                          correct, the only files listed in the first example 
                          should be non-archived files and in the second example 
                          the only files should be directory entries. 
                         This inconsistency is the main reason why I ended up 
                          creating a FindFirst() 
                          and FindNext() example 
                          program. I use the sample program to test which parameters 
                          should be used and the result for various test directories. 
                         This 
                          article was written by Curtis Krauskopf (email at Copyright 2003-2010 The Database Managers, Inc. ). 
  Popular C++ topics at The Database Managers: |