Reputation: 5537
It seems like FileSystem.GetFiles() is unable to recover from the UnauthorizedAccessException exception that .Net triggers when trying to access an off-limit directory.
In this case, does it mean this class/method isn't useful when scanning a whole drive and I should use some other solution (in which case: Which one?)?
Here's some code to show the issue:
Private Sub bgrLongProcess_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgrLongProcess.DoWork
Dim drive As DriveInfo
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
'Scan all fixed-drives for MyFiles.*
For Each drive In DriveInfo.GetDrives()
If drive.DriveType = DriveType.Fixed Then
Try
'How to handle "Access to the path 'C:\System Volume Information' is denied." error?
filelist = My.Computer.FileSystem.GetFiles(drive.ToString, FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
For Each filepath In filelist
DataGridView1.Rows.Add(filepath.ToString, "temp")
'Trigger ProgressChanged() event
bgrLongProcess.ReportProgress(0, filepath)
Next filepath
Catch Ex As UnauthorizedAccessException
'How to ignore this directory and move on?
End Try
End If
Next drive
End Sub
Thank you.
Edit: What about using a Try/Catch just to have GetFiles() fill the array, ignore the exception and just resume?
Private Sub bgrLongProcess_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgrLongProcess.DoWork
'Do lengthy stuff here
Dim filelist As Collections.ObjectModel.ReadOnlyCollection(Of String)
Dim filepath As String
filelist = Nothing
Try
filelist = My.Computer.FileSystem.GetFiles("C:\", FileIO.SearchOption.SearchAllSubDirectories, "MyFiles.*")
Catch ex As UnauthorizedAccessException
'How to just ignore this off-limit directory and resume searching?
End Try
'Object reference not set to an instance of an object
For Each filepath In filelist
bgrLongProcess.ReportProgress(0, filepath)
Next filepath
End Sub
Upvotes: 0
Views: 7302
Reputation: 1
I use the following functions to search all the drives located on a system for specific files, and it does not break when 'GetFiles' hits an execption. This is done by performing a top level only search with 'GetDirectories' which will give you a base level directory list, and if it does not throw an execption a 'GetFiles' search of all subdirectories of that directory is performed, if an execption is hit then then the 'GetDirectories' directory is located, and the process continues. After each successfully search performed by 'GetFiles' the directories that contain the files been searched for are stored in a list. Once all the existing drives located on the system have been searched, the main function concludes it operation by returning the data that it has added to the list.
Public Function Left(ByVal TextString As String, ByVal LocateString As String) As String
Try
Left = Microsoft.VisualBasic.Left(TextString, InStrRev(TextString, LocateString) - 1)
Catch ex As Exception
Left = TextString
End Try
End Function
Public Function GetExistingDrives() As List(Of String)
Dim LocatedDrives As New List(Of String), DriveInformation As System.IO.DriveInfo() = System.IO.DriveInfo.GetDrives
For Each FoundDrive As System.IO.DriveInfo In DriveInformation
Try
LocatedDrives.Add(UCase(Left(FoundDrive.Name, "\")))
Catch ex As Exception
End Try
Next
GetExistingDrives = LocatedDrives
End Function
Public Function LocateFiles(ByVal ParamArray SearchPattern As String()) As List(Of String)
Dim LocatedDirectoriesList As New List(Of String), LocatedFilenameList As System.Collections.ObjectModel.ReadOnlyCollection(Of String)
For Each DriveLetter In GetExistingDrives()
Try
For Each SearchDirectory As String In My.Computer.FileSystem.GetDirectories(DriveLetter, FileIO.SearchOption.SearchTopLevelOnly)
Try
LocatedFilenameList = My.Computer.FileSystem.GetFiles(SearchDirectory, FileIO.SearchOption.SearchAllSubDirectories, SearchPattern)
Catch ex As Exception
End Try
If (LocatedFilenameList.Count <> 0) Then
For Each LocatedFilename As String In LocatedFilenameList
Dim LocatedDirectory As String = Left(LocatedFilename, "\")
If (LocatedDirectoriesList.IndexOf(LocatedDirectory) = -1) Then LocatedDirectoriesList.Add(LocatedDirectory)
Next
End If
Next
Catch ex As Exception
End Try
Next
LocateFiles = LocatedDirectoriesList
End Function
Upvotes: 0
Reputation: 1271
Put your try catch statement within your For each filepate in filelist
loop. Because right now, when you catch UnauthorizedAccessException
, you skip out on the rest of the filelist
items.
Edit
You are right. A try-catch is expensive when an exception is thrown, and usually you want to try and detect that situation before the exception is thrown. One way to do that in this situation, is to check for Access to the file, before doing anything with it.
For directories, there is this GetAccessControl function. There is a similar function for Files.
You may have to break your GetFiles function to only get Directories initially, then loop through each directory recursively always calling GetAccessControl
for each directory and file.
Upvotes: 1
Reputation: 5537
For those interested, I found the following working solution:
http://dotnetperls.com/recursive-file-directory-vbnet
If you want to filter the search to a given type of files (eg. "MyFiles.*"), change
Public Shared Function GetFilesRecursive(ByVal initial As String, ByVal extension As String) As List(Of String)
... so you can call it this way:
Dim list As List(Of String) = FileHelper.GetFilesRecursive("C:\", "MyFiles.*")
Upvotes: 0