I was looking for a task to use as the background task of my recent article on the BackgroundWorker component and decided that I'd use a recursive search through folders. Ironically, I ended up spending about twice as much time on getting the recursive search procedures running correctly than I spent on the main BackgroundWorker code.
As I ended up with three varieties of recursive searches - and found some things that should have worked, but didn't seem to - I thought I'd blog it.
To find a particular file type in folders and sub-folders I used this code:
Sub FileFinder(ByVal dir As String)
Try
' Display all files in a directory that match file type
For Each fname As String In IO.Directory.GetFiles(dir)
If fname.EndsWith("txt") Then
' pass back progress message
Console.WriteLine(fname)
End If
Next
' A recursive call for all the subdirectories in this directory.
For Each subdir As String In IO.Directory.GetDirectories(dir)
FileFinder(subdir)
Next
Catch ex As Exception
MessageBox.Show(ex.Message.ToString)
End Try
End Sub
You'll probably have spotted that I used the "EndsWith" function there to test for the tile type I'm interested in. Originally I tried using the SearchPattern parameter of the GetFiles method, but for whatever reason it didn't identify the files for me.
Another little glitch to keep in mind is that there may be some files to which users don't have authorised access. If for example you call the above code and pass in "C:\" as the argument you will probably get one or more "Access Denied" messages via the Exception Handler. Of course, because the exception is being handled, the user can click on the messagebox's OK button and the procedure will continue to run.
Obviously, to avoid the message appearing at all you can just comment out the MessageBox.
If you want to home in on that particular issue and allow access but still want to know if other kinds of problems occur then you can fine tune your structured exception handlers (as of course you should) and include a handler specifically aimed at the access exception:
Catch ioex As System.UnauthorizedAccessException
' Access Denied Problem will be handled here
Catch ex As Exception
' Other Exceptions will be flagged up to users
MessageBox.Show(ex.Message.ToString)
Hard coding the file type limits reusability, so a better version would be:
Sub FileTypeFinder(ByVal dir As String, ByVal FileExt As String)
Try
' Display all files in a directory that match file type
For Each fname As String In IO.Directory.GetFiles(dir)
If fname.EndsWith(FileExt) Then
' pass back progress message
Console.WriteLine(fname)
End If
Next
' A recursive call for all the subdirectories in this directory.
For Each subdir As String In IO.Directory.GetDirectories(dir)
FileTypeFinder(subdir, FileExt)
Next
Catch ioex As System.UnauthorizedAccessException
Catch ex As Exception
MessageBox.Show(ex.Message.ToString)
End Try
End Sub
The client now passes in the file type as well as the parent folder.
The final version I used was one which recursively searched through folders and subfolder, found files that matched a particular file extension and then searched inside matching files for a particular string. This of course could be a particularly long task in some cases and so a good candidate for the BackgroundWorker.
Private Sub FileContentFinder(ByVal dir As String, ByVal FileExt As String, _
ByVal TargetText As String)
Dim rdr As IO.StreamReader
Try
' Display all files in a directory that match file type
For Each fname As String In IO.Directory.GetFiles(dir)
If fname.EndsWith(FileExt) Then
rdr = New IO.StreamReader(fname)
Dim strFileContents As String = rdr.ReadToEnd
rdr.Close()
rdr = Nothing
' If this file contains the target string, pass back progress message
If strFileContents.Contains(TargetText) Then
Console.WriteLine(fname)
End If
End If
Next
' A recursive call for all the subdirectories in this directory.
For Each subdir As String In IO.Directory.GetDirectories(dir)
FileContentFinder(subdir, FileExt, TargetText)
Next
Catch ex As Exception
MsgBox(ex.Message.ToString)
End Try
End Sub
There are many other twists you could use on the above approaches, such as testing against file attributes for dates, for example, or copying files that meet certain criteria.