Managed Extensions: Finding Files by Extension

Welcome to this week's installment of .NET Tips & Techniques! Each week, award-winning Architect and Lead Programmer Tom Archer demonstrates how to perform a practical .NET programming task using either C# or Managed C++ Extensions.

A reader recently asked me how to locate all files with a specific extension so that he could register those files with his application. Basically, all he needs to do is allow the user to specify where he wants the search to begin (accomplished with the .NET FolderBrowserDialog component) and then have a recursive function enumerate each folder and compare the searched-for file extension with the extension of each file found. The "matching" files are returned to the caller in an ArrayList object. Figure 1 shows an example of a demo application I wrote to illustrate this:

Folder-Browser Dialogs in .NET

Many C++ classes today allow you to display a folder-browser dialog. However, if you had to perform this task (using the Shell API function SHBrowseForFolder) before C++ helper classes and better documentation existed, you'll be happy to note that this task is much easier in .NET (specifically, Windows Forms applications). For this article's demo, I did the following:

  1. I opened the form whose class will use the folder browser.
  2. I opened the Toolbox and dragged a FolderBrowserDialog control onto the form. (I positioned it below the form because it's a class and not a UI element.)
  3. I specified the needed properties. IntelliSense helped here, but the most commonly specified ones were things like Description (the text that will display on the dialog above the folder list), ShowNewFolderButton (a property that allows you to specify if you want the user to be able to create folders while browsing), and RootFolder (whose value is used to specify the top-level folder that the user cannot exceed). The demo's Form::Load method has the following initialization:
    private: System::Void Form1_Load(System::Object *  sender,
      System::EventArgs *  e)
    {
    ...
    browser->Description = S"Select the directory that you want
                             to use as
                             the starting point for the search";
    browser->ShowNewFolderButton = false;
    browser->RootFolder = Environment::SpecialFolder::Desktop;
    ...
    
  4. Once initialized, the following code invokes the FolderBrowserDialog dialog and uses the user-selected path to update a Label (static) control on the form:
    private: System::Void folderBrowserButton_Click(System::Object
             *  sender, System::EventArgs *  e)
    {
      System::Windows::Forms::DialogResult result = browser->
                                                    ShowDialog();
      if (DialogResult::OK == result)
        startingFolder->Text = browser->SelectedPath;
    }
    

    Figure 2 shows the result.

Searching for Files

Once I had the basic demo UI in place, I wrote a very simple class (FileSearcher) that implements a single static method (FindByExtension) to allow a caller to search for files in a specified folder (and optionally, its subfolders). Here are the basic steps that accomplished the task:

  1. Call the System::IO::Directory::GetFiles method to retrieve all files for a specified folder. The files' names are returned in an ArrayList object where each file is a String object.
  2. Enumerate the returned ArrayList object by using a for loop.
  3. For each String (file name) in the ArrayList, call the Path::GetExtension method to return the file name's extension and then compare it (ignoring case) to the caller-specified extension. If the strings match, the file name is added to the caller's passed ArrayList object.
  4. Once the current folder has been enumerated, the method checks the Search-Subfolders flag. If it is off, the method returns. If it is on, the method calls the System::IO::Directory::GetDirectories method to retrieve all folders for the current folder. These folders are then enumerated and the FileSearcher::FindByExtension method calls itself for each found folder.

Here's the code from the demo's Form1.h file:

__gc class FileSearcher
{
public:
  static void FindByExtension(String* startingFolder,
                              String* extension,
                              ArrayList* matchingFiles,
                              bool recurseSubFolders)
  {
    String* fileList[] = Directory::GetFiles(startingFolder);

    String* fileName;

    // search current folder
    for (int i = 0; i < fileList->Count; i++)
    {
      fileName = static_cast<String*>(fileList->Item[i]);

      if (0 == String::Compare(Path::GetExtension(fileName),
          String::Format(S"{0}{1}", 
                  extension->StartsWith(S".") ? S"" : S".",
                  extension), true))
          matchingFiles->Add(fileName);
    }

    if (recurseSubFolders)
    {
      // for each subfolder
      fileList = Directory::GetDirectories(startingFolder);
      for (int i = 0; i < fileList->Count; i++)
      {
        FindByExtension(static_cast<String*>(fileList->Item[i]),
                        extension, 
                        matchingFiles,
                        true);
      }
    }
  }
};

The client then uses the FileSearcher class like this (where the value being passed to the FindByExtension method is being retrieved from the form):

ArrayList* matches = new ArrayList();
FileSearcher::FindByExtension(startingFolder->Text,
                              extensionToFind->Text,
                              matches,
                              searchSubfolders->Checked);

listBox1->Items->Clear();

for (int i = 0; i < matches->Count; i++)
{
  listBox1->Items->Add(matches->Item[i]);
}

Areas That Can Be Improved

While this article has illustrated how to display a folder-browser dialog and how to search a user's folder(s) for files matching a specified extension, the solution has plenty of room for improvement. (Isn't there always?) Mainly, the FileSearcher class could be augmented to support multiple extensions, as well as asynchronous delegates and events to notify the caller of progress. That way, the caller isn't blocked on the call to the FindByExtension method and can display a progress bar, as well as allow the user to cancel what could be a very lengthy operation.

Not only would that flesh out the FileSearcher class, making it more usable, but it would also represent a great example of where and how asynchronous delegates and events can be used. Depending on the feedback I get from this article, I'll do that article at some time in the future.



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.

Downloads

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

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Relying on outside companies to manage your network and server environments for your business and applications to meet the needs and demands of your users can be stressful. This is especially true as many Managed Hosting organizations fail to meet their service level agreements. Read this Forrester total economic impact report and learn what makes INetU different and how they exceed their customers' managed hosting expectations.

Most Popular Programming Stories

More for Developers

RSS Feeds