Enumerating Files and Directories in VB 2010

Introduction

Microsoft has called me a few times in the past for an interview. Once to work for the ASP.NET team and once to work for what used to be called the national practices services, part of Microsoft Consulting, and for a while I worked as a consultant for Microsoft consulting services.

Microsoft is building software for general consumption, or thinking of it another way, for the widest audience possible. Part of my interview of the ASP.NET was how to build software for general consumption. The challenge with general consumption software is that a developer may want to make things easier, but easy software sometimes opens some doors and closes others. I think this is what happened with Directory.GetFiles and Directory.GetDirectories.

GetFiles and GetDirectories replaced the API methods findfirst and findnext to make traversing the file system easier. The drawback with GetFiles and GetDirectories was that these methods returned arrays, and the code had to wait until the entire array was populated before the calls returned. To eliminate the wait GetFiles and GetDirectories have newer versions named EnumerateFiles and EnumerateDirectories. You no longer have to wait for EnumerateFiles and EnumerateDirectories to return to begin using the returned files and directories, but there are a couple of minor obstacles involved here to.

With EnumerateFiles and EnumerateDirectories you don't have to map API calls, write your own recursive descent to map items in sub-folders, and you don't have to wait for the entire enumeration to finish to interact with the results. The obstacle is that if you encounter an error, for example when you enumerate folders and child folders, the EnumerateXxx method will throw an exception, and the enumeration will fail. This makes it difficult to do something as seemingly simple as create a file system browser with these methods. There is hope though.

Let's take a look at EnumerateFiles and EnumerateDirectories, how to use LINQ, and exception handlers to walk the file system even in the face of access exceptions.

Using EnumerateFiles and EnumerateDirectories

A version of EnumerateFiles and EnumerateDirectories exists in both the Directory and DirectoryInfo classes in the System.IO namespace. The version in the Directory class can traverse sub-directories and returns just the names of the matched items. If you need the file or directory information then use the DirectoryInfo version. Both EnumerateFiles and EnumerateDirectories have several overloaded versions that accept a variation of arguments including the starting path, the file mask, and a SearchOption enumeration argument that determines whether just the top folder or all folders are searched.

The following code uses Directory.EnumerateFiles to return the files in the root of the C: drive. The resultant enumeration is used as a source in a LINQ query. (In this case it is worth noting that the LINQ query doesn't filter the data so it is superfluous; that is you could just use Directory.EnumeratoryFiles by itself.) The Array.ForEach method converts the enumeration to an array, a requirement of the ForEach method, and a Lambda expression to write the results to the console.

  Dim files = From file In Directory.EnumerateFiles("C:\")
              Select file
  Array.ForEach(files.ToArray(), Sub(f) Console.WriteLine(f))

In such a simple use case EnumerateFiles and EnumerateDirectories are easy to use. This is what we want from a framework.

Handling EnumerateDirectories Exceptions

If you call Directory.EnumerateDirectories with the SearchOption.AllDirectories argument then EnumerateDirectories will perform a directory traversal for you, which includes sub-folders. However, if you start at a high level directory like the root then you are likely to encounter a folder that will cause an access exception. You won't know this until you touch the directory data though. For example, if you write:

  Dim d = Directory.EnumerateDirectories("C:\", "*.*", SearchOption.AllDirectories)

Then the code will compile and run. If you use the results as the source for a for loop such as the following:

  ' Get all directories
  For Each d In Directory.EnumerateDirectories("C:\", "*.*", SearchOption.AllDirectories)
    Console.WriteLine(d)
  Next

Then the code may throw an UnauthorizedAccessException when it hits a folder like C:\Documents and Settings. To catch the exception wrap the for loop in a Try Catch block. Here is the revised code.

  Try
    For Each d In Directory.EnumerateDirectories("C:\", "*.*", SearchOption.AllDirectories)
      Console.WriteLine(d)
    Next
  Catch ex As Exception
    Console.WriteLine("Handle exception here")
  End Try

You can use a broader exception class like Exception to catch all possible exceptions or multiple catch blocks if you want to handle individual kinds of exceptions differently.

A potential challenge here is that if you actually want to display or capture all folders even if access is denied then you need a slightly different approach than simply using the SearchOption.AllDirectories argument.

Searching All Folders Without Failing on Exceptions

Suppose you want to traverse all folders. Suppose further that if you encounter an exception you want to skip that folder and continue. Until there is an option like SearchOption.ContinueOnErrors or an attachable event that performs a semantically similar operation there is no opportunity to continue on an exception when it only takes one method call to traverse sub-folders. For this reason you need to split the call up.

To effectively skip folders and continue you can combine a Stack object and obtain top level folders. Next, pop each folder off the stack and try to query the sub-folders, repeat the pushing and popping of the stack until all folders and sub-folders have been traversed. Because this approach splits the traversal into an outer loop to manage the stack you can handle an exception and continue processing the items in the stack. Listing 1 provides a solution that will grab unauthorized folders-for example for your file system management tool-too and continue processing other folders.

  Imports System.IO
  Imports System.Security
  
  Module Module1
  
      Sub Main()
        Dim results As List(Of String) = New List(Of String)
        Dim start As String = "c:\"
        results.Add(start)
        Dim stack As Stack(Of String) = New Stack(Of String)
  
        Do
          Try
            Debug.WriteLine(start)
  
            Dim dirs = From d In Directory.EnumerateDirectories(start,
              "*.*", SearchOption.TopDirectoryOnly)
              Select d
  
            ' multline Lambda - don't really need this
            Array.ForEach(dirs.ToArray(), Sub(d)
              stack.Push(d)
            End Sub)
  
            start = stack.Pop()
            results.Add(start)
  
          Catch ex As UnauthorizedAccessException
            Console.WriteLine(ex.Message)
            start = stack.Pop()
            results.Add(start)
          End Try
  
        Loop Until (stack.Count = 0)
  
        For Each d In results
            Console.WriteLine(d)
        Next
  
        Console.ReadLine()
  
      End Sub
  End Module

Listing 1: Using a Stack to track top-level folders and then process sub-folders one at a time in the event of an exception.

The List(Of String) will contain the found directories. The variable start will start the search at the root of the C: drive. The stack is used to store the directories that need to be searched. The Do loop starts the process and the Try begins the exception handling block.

The statement beginning with Dim dirs begins searching the sub-folders in the folder assigned to start. SearchOption.TopDirectoryOnly will return the folders in start. (You could skip the LINQ query if you didn't want to perform an additional filtering, but the code does demonstrate how to incorporate LINQ if you need to.) The Array.ForEach pushes all of the child folders on the stack using multi-line Lambda sub-expression syntax. Next, the stack is popped, the next folder is stored in the results list, and the process continues if the stack is not empty. If an exception is thrown the stack is popped again and the next item is stored.

The way this solution is written and by starting at the root this will be a long running process. However, since the solution is broken up into multiple stages you have plenty of opportunities to interact with the results along the way and you can even split the solution into a background process--which is probably what Windows Explorer does.

Summary

EnumerateFiles and EnumerateDirectories make it really easy to get file and directory names and information. The most common complaint on the web is that these methods stop processing on an exception. By splitting the call up by directories you can manage exceptions in an outer loop. The online response to aborted processing is to modify the EnumerateXxx methods to incorporate continue behavior.

Related Articles



About the Author

Paul Kimmel

Paul Kimmel is the VB Today columnist for CodeGuru and has written several books on object-oriented programming and .NET. Check out his upcoming book Professional DevExpress ASP.NET Controls (from Wiley) now available on Amazon.com and fine bookstores everywhere. Look for his upcoming book Teach Yourself the ADO.NET Entity Framework in 24 Hours (from Sams). You may contact him for technology questions at pkimmel@softconcepts .com. Paul Kimmel is a Technical Evangelist for Developer Express, Inc, and you can ask him about Developer Express at paulk@devexpress.com and read his DX blog at http:// community.devexpress.com/blogs/paulk.

Comments

  • Slight Error in logic

    Posted by Good Example on 12/28/2013 03:57pm

    Just trying out this code, when the line 25 start = stack.Pop() gets hit for the last folder the loop until (stack.count = 0) becomes true which means that the last folder is never processed for additional folders or files.

    Reply
  • Replica Oakley Pit Bull no tax worldwide

    Posted by exykiifxp on 06/27/2013 02:47pm

    Fake Ray Ban Sunglasses ,The style on the Oakley sunglasses inject tone cool just like the style, functionality and security caused by a volume of special features. Generally speaking, the content of Oakley sunglasses, feather weight and impact protection features. Long-lasting hitting the ground with solar radiation, may also cause cataracts, the lens cover of Besides rapid long-term harm to our eyes, like snow blindness vision. Oakley Outlet ,Oakley sunglasses has become arrive at the goal of protection, contact wrapped around repel much more gentle and UV protection to obtain additional the sun's rays compared to a normal light wear, and will toss an array of broad-brimmed head. Oakley now offers other kinds of sunglasses like skiing and snowboarding to help keep sports fans to go into the adventure undertake a clearer perception of sports. Cheap Oakley Eyepatch 2 ,These Maui humble glasses is basically polarized poor coloring from the decline in the actual glare, therefore you think you only colored areas, instead of immediately in a luxury: OAKLEY different style glasses. In the design of the year sunglasses. Nostalgic retro style round glasses, exaggerated style is much fun. To recognise the blue, straw and pink lenses aren't suitable for wear in bright light, as their anti-reflective capacity is weak. This will be the best option for the particular face shape, Iridium lens coating reduces glare and adjusts the transmission, and really create almost any lighting conditions are appropriate. When you are wanting to hone the top for your specific sport or Applied Optics, check out the optional lens with this high-quality performance sunglasses set. Note can not see ultraviolet, the approval Oakley sunscreen, to eliminate the sun's ultraviolet power, for example, might be more harmful ultraviolet light, wil attract. Oakley sunglasses use out from the lens is actually quite easy Switchlock technology, that is a modern quick release system locks the lens in situ. Recently introduced the "wearable electronics" to collect weapons to deliver Bluetooth solutions, and implantation in the solution framework to eliminate the necessitie in the headset, headphone, line and wire. The increase in personal inside your clothing line, so you feel safe clothing and accessories, good deal point in addition to simple supply in the interest on fashionable women's equipment and females, the popularity of clothing is important. You cannot squeeze price now a wide variety of brands of quality in order to fight for ones attention, choosing hard to know what to decide on, which help you additional money.

    Reply
  • hackett outlet

    Posted by gogogyn on 05/13/2013 05:10am

    I like this weblog so considerably, bookmarked. oakley outlet uk oakley australia ADS Typical Characters of Oakley Sunglasses

    Reply
  • How to?

    Posted by Oscar Rodriguez on 02/06/2013 06:41pm

    I'm trying to get folder size values from 5 different directories that has a total of (aprox) 5TB. Actually I have this code, that, as I can see, correspond to the old API. How can I improve the performance of the code? Your method do an array first isn't? Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim PDRFolder As New DirectoryInfo("C:\InputMedia") Dim filesInfo9() As FileInfo = PDRFolder.GetFiles("*.*", SearchOption.AllDirectories) Dim fileSizePDR As Long = 0 Dim fileSize9 As Long = 0 'Inicia el calculo de consumo para Playout PDR Folder For Each fileInfo9 As FileInfo In filesInfo9 fileSize9 += fileInfo9.Length Next fileSizePDR = fileSize9 lblBytes_PDR.Text = fileSizePDR.ToString lblSize_PDR.Text = FormatNumber(fileSizePDR / (1024 * 1024 * 1024), 2) + " GB" + " (" + lblBytes_PDR.Text + " bytes)" lblFolder_PDR.Text = PDRFolder.ToString lblHours_PDR.Text = CStr(FormatNumber((fileSizePDR / (1024 * 1024 * 1024)) / 15, 2)) 'Conversion a formato de tiempo para Playout PDR Folder Dim value9 As Decimal = lblHours_PDR.Text Dim ts9 As TimeSpan = TimeSpan.FromHours(value9) Label5.Text = (String.Format("{0}h {1}m {2}.{3}s", ts9.Hours, ts9.Minutes, ts9.Seconds, ts9.Milliseconds)) hor5 = fileSizePDR 'Fin del calculo de consumo para Playout PDR Folder If Val(lblSize_PDR.Text) 2 Then pbPDR.Visible = True 'Muestra la alarma en caso el tama;o sea mayor tbLog.Text &= System.DateTime.Now.ToString & vbCrLf tbLog.Text &= "Límite de almacenamiento superado en PDR" & vbCrLf tbLog.SelectionStart = tbLog.TextLength tbLog.ScrollToCaret() Else pbPDR.Visible = False End If End Sub

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

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • QA teams don't have time to test everything yet they can't afford to ship buggy code. Learn how Coverity can help organizations shrink their testing cycles and reduce regression risk by focusing their manual and automated testing based on the impact of change.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds