Search Entire Directories for Specified Files with Managed C++

I recently designed and coded a popular anti-malware (spyware) application that, among other tasks, searched for known malware "fingerprints" in memory, on disk, and in the Windows Registry. To search for the files on disk, I wrote a recursive function that searches a given file starting at a specified folder. The function then calls itself for each folder it finds until it has searched the entire folder hierarchy. This week's column illustrates this function.

Enumerating Directories and Files with .NET

An MFC developer would enumerate directories in search of specific files, typically via the CFindFile class or by using the Win32 FindFirstFile and FindNextFile functions. In .NET, this task is accomplished via the DirectoryInfo and FileInfo classes.

The DirectoryInfo class is very easy to use and, for this particular task, requires the knowledge of only a few methods and properties:

  • Instantiation–To create an instance of the DirectoryInfo class, simply pass the directory or folder for which you need to retrieve information.
  • Verification directory exists–After instantiating the DirectoryInfo object and before using any of its method or properties, you should verify that the specified directory exists. To do that, check that the DirectoryInfo::Exists property is set to true.
  • Enumerating a directory's files–The DirectoryInfo class defines a method called GetFiles that returns an array of objects of type FileInfo. Each FileInfo object then contains information about that file such as the file name, its attributes, creation, last modified timestamps, and so on.
  • Enumerating a directory's "sub-directories"–The DirectoryInfo class defines a method called GetDirectories that returns an array of objects of type DirectoryInfo, where each object represents a sub-directory.

Generic Function to Search for Files

Now that you know the basics of the .NET classes needed to enumerate directories and files, you can easily write a generic function to search a given directory path for a specified file. To that end, the FindFile function takes the following parameters:

  • startingDir–The directory where you want the search to begin. (Because this function calls itself recursively, this value will change to represent the current directory being searched.)
  • fileName–The file name for which the function is being called.
  • foundFiles–Because more than one instance of the searched-for file name might be located when searching a directory structure, this object (of type ArrayList) will contain the found files. As with the startingDir parameter, this value is constantly updated as the FindFile function calls itself for each sub-directory.

As you can see in the following code, the FileFind function first retrieves the DirectoryInfo object for the specified directory (startingDir):

void FindFile(String* startingDir, String* fileName, ArrayList* foundFiles)
{
  DirectoryInfo* dirInfo = new DirectoryInfo(startingDir);
  if (dirInfo->Exists)    // make sure directory exists
  {
    FileInfo* files[] = dirInfo->GetFiles();
    for (int i = 0; i < files->Length; i++)
    {
      String* currFileName = files[i]->ToString();
      if (0 == String::Compare(currFileName, fileName, true))
      {
        foundFiles->Add(files[i]);
      }
    }

    DirectoryInfo* subDirs[] = dirInfo->GetDirectories();
    for (int i = 0; i < subDirs->Length; i++)
    {
      String* dirName = subDirs[i]->FullName;
      FindFile(dirName, fileName, foundFiles);
    }
  }
}

Once the DirectoryInfo::Exists property is checked to ensure that the directory exists, the directory's files are retrieved into a FileInfo array. This array is enumerated and each file's name is compared to the caller's specified file name. If the file names match, the FileInfo object is added to the ArrayList, which the caller will ultimately enumerate when the function has completed. (For the anti-malware application, I had to also compare MD5 hash-code values when a matching file name was found. However, as most people don't need this added functionality, the FindFile function matches only on file name.)

Once all the files have been processed for the current directory, the sub-directories are retrieved and the FindFile function is then called for each of these sub-directories. This process enables the FindFile function to correctly search an entire directory hierarchy for all instances of the caller's specified file name.

Now, all the caller has to do is to instantiate an ArrayList object, pass it to the FindFile function, and then enumerate the ArrayList object upon return:

ArrayList* foundFiles = new ArrayList();
FindFile(S"d:\\acg\\legal", S"eula.txt", foundFiles);

if (foundFiles->Count)
{
  for (int i = 0; i < foundFiles->Count; i++)
  {
    FileInfo* fileInfo = 
      static_cast<FileInfo*>(foundFiles->get_Item(i));
    Console::WriteLine(fileInfo->FullName);
  }
}
else
{
  Console::WriteLine("SNo matching files found");
}


About the Author

Tom Archer - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's competitive marketplace requires the organization to frequently release and deploy applications at the pace of user demands, with reduced cost, risk, and increased quality. This book defines the basics of application release and deployment, and provides best practices for implementation with resources for a deeper dive. Inside you will find: The business and technical drivers behind automated application release and deployment. Evaluation guides for application release and deployment solutions. …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds