Click here to Skip to main content
15,887,214 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
OK, short (?) story:

Trying to read the folders and subfolders for a given drive using vb.net and VS2022. Many online posts recommend use of Directory.GetDirectories (some going back 20+ years), but it generates errors when a directory is not accessible (access denied) and apparently you can't use TRY CATCH END TRY to fix the issue with .GetDirectories.

Microsoft has a web page that recommends the use of the Directory.EnumerateDirectories method that can be used with TRY CATCH END TRY instead of .GetDirectories.

So this method catches the folders that are not accessible (first problem solved!), but now once I step through the values generated by Directory.EnumerateDirectories, one or more NULL values apparently are stuck in there somewhere. I thought that using TRY CATCH could eliminate this problem, but apparently not.


The code that calls the function is:

Dim directories As List(Of String) = EnumerateDirectories(path)

The function EnumerateDirectories(path) is:
VB
Public Shared Function EnumerateDirectories(path As String) As IEnumerable(Of String)

	Try
		Dim dirPath As String = "C:\"
		Dim dirs As List(Of String) = New List(Of String) _
		    (Directory.EnumerateDirectories(dirPath, "*", _
			    SearchOption.AllDirectories))
		Return dirs

	Catch ex As UnauthorizedAccessException
		MessageBox.Show(ex.Message)

	Catch ex As ArgumentException
		MessageBox.Show(ex.Message)

	End Try

End Function


What I have tried:

Two more things to note:
1) VS 2022 does provide the warning:

BC 42105 Function 'EnumerateDirectories' doesn't return a value on all code paths. A null reference exception could occur at run time when the result is used.

Now I did figure out that since this is a function, I need to return something under each CATCH; however, I am not sure what to put there. I *assume* that the function has found a NULL value instead of a folder name (string); somehow, you can tell it to replace the NULL value with a valid string, such as " ". The MessageBox.Show() do not pop up under any CATCH condition.

2) So I use the following code to retrieve the folder names with the following:

Dim directories As List(Of String) = EnumerateDirectories(path)

The code is good up to the point of accessing each value in 'directories'. The following runtime error pops up when I enter a loop to work with each entry in 'directories' (in the main sub, not in the function):

System.NullReferenceException: 'Object reference not set to an instance of an object.'


Overall: I assume this all means that when EnumerateDirectories is doing its function to determine all folders and subfolders for a drive, it is getting a NULL value (or more) instead of a string value. I have tried putting FOR EACH loops in various places to try to replace the NULL value with " ", but to no avail.

Suggestions? Needless to say, I do not make my living as a code developer.
Posted
Updated 24-Mar-24 15:37pm
v2
Comments
Graeme_Grant 24-Mar-24 21:35pm    
Where is the error being thrown? Which line of code? Make sure that you "break on all errors" = Debug > Windows > Exception Settings > Common Language Runtime Exceptions = ticked (default is indeterminate). Use the "Improve question" link provided above to post additional information requested.

Also, not all code paths return a list. You should return an empty list before End Function otherwise the return value can be null.

It doesn't work because the EnumerateDirectories function will stop at the first sign of trouble AND NOT RESUME. It's not designed to enumerate entire drives of directories.

To fix your code, you're going to have to write your own EnumerateDirectories method. You can easily do this with a recursive function that adds directories to a collection and returns the collection at the end.
VB.NET
    Dim list As New List(Of String)

    GetDirectories("C:\", list)

    For Each dir As String In list
        Console.WriteLine(dir)
    Next
.
.
.
    Sub GetDirectories(rootFolder As String, ByRef collection As List(Of String))
        If Directory.Exists(rootFolder) Then
            ' The Try/Catch block will handle cases where we can't
            ' go into the directory to get it's sub directories.
            Try
                Dim directories As String() = Directory.GetDirectories(rootFolder)

                ' Go into each sub directory and call ourselves to keep
                ' adding sub directories of the current root directory.
                For Each dir As String In directories
                    ' Add each found sub directory to the collection.
                    collection.Add(dir)

                    ' Call ourselves here using each sub directory as the
                    ' new "root" directory.
                    GetDirectories(dir, collection)
                Next
            Catch
                ' Ignore any problems
            End Try
        End If
    End Sub
 
Share this answer
 
I fired up VS and used your code in a console app. I modified it slightly with optimisations:
VB.NET
Imports System.IO

Module Program
    Sub Main()
        Dim directories As IEnumerable(Of String) = GetDirectories("c:\")

        If directories IsNot Nothing Then
            For Each directory As String In directories
                Console.WriteLine(directory)
            Next
        End If

        Debugger.Break()

    End Sub

    Iterator Function GetDirectories(path As String) As IEnumerable(Of String)

        Dim dirs As IEnumerable(Of String) = Nothing

        Try
            dirs = Directory.EnumerateDirectories(path)

        Catch ex As Exception
            Console.WriteLine($"{ex.GetType().Name}: {ex.Message}")

            'Catch ex As UnauthorizedAccessException
            '    Console.WriteLine(ex.Message)

            'Catch ex As ArgumentException
            '    Console.WriteLine(ex.Message)

            'Catch ex As DirectoryNotFoundException
            '    Console.WriteLine(ex.Message)

            'Catch ex As IOException
            '    Console.WriteLine(ex.Message)

            'Catch ex As PathTooLongException
            '    Console.WriteLine(ex.Message)

            'Catch ex As SecurityException
            '    Console.WriteLine(ex.Message)
        End Try

        If dirs IsNot Nothing Then
            For Each dir As String In dirs
                Yield dir
            Next
        End If

    End Function

End Module

I have added a null check and ensured all code paths return something. As I am using a Yield Statement - Visual Basic | Microsoft Learn[^], if their is nothing yield, it will return a null. If you do not want to return a null, then you could return an empty string:
VB
If dirs IsNot Nothing Then
    For Each dir As String In dirs
        Yield dir
    Next
Else
    Yield String.Empty
End If


Here is an updated version to return all directories and subdirectories:
VB
Iterator Function GetDirectories(path As String) As IEnumerable(Of String)

    Try
        For Each dir As String In Directory.EnumerateDirectories(path)
            Yield dir
            For Each subdir As String In GetDirectories(dir)
                Yield subdir
            Next
        Next
    Catch ex As Exception
        Console.WriteLine($"{ex.GetType().Name}: {ex.Message}")
    End Try

End Function

The above directory walker is extremely fast and highly memory efficient.

I Hope this has answered your questions.
 
Share this answer
 
v4
Comments
lewist57 25-Mar-24 12:28pm    
Thanks to those who posted solutions. Using the second set of code produced positive results.

I say "positive" in that my overall program searches a drive for directories, subdirectories and files, and loads nodes on a treeview control. The code works fine on the D:\ drive and two Google Drives (G:| and H:\) but runs out of memory on my C:\ drive, hangs up, and kicks out the following error:

System.OutOfMemoryException: 'Exception of type 'System.OutOfMemoryException' was thrown.'

I am sure that it can take some more refinement to eliminate this error, but it works great otherwise. Perhaps I will get the program in working shape and post an article on CodeProject, as even though it is now 2024, there are those (like me) who still want to use VB.net.

Really makes one appreciate OpenFileDialog control now.
Graeme_Grant 25-Mar-24 19:21pm    
Glad that it helps.

Regarding out-of-memory, do loading on demand with the TreeView control:
1. Load the root + 1
2. in the +1, only add a stub for each sub folder if it has 1 or more sub directories, no stub fi no sub directories.
3. on expand, remove the stub and repeat - load that path, then + 1 for each sub folder with a stub.

This is called virualization and will instantly load like the OpenFileDialog control. 😊
Well they say those that don't remember history are condemned to repeat it, and I guess that is true here.

The following article does exactly what I was trying to do (with the exception of creating a .dll) from over 20 years ago:

https://www.codeproject.com/Articles/4191/FilesystemTree-Control-in-NET

It was easily converted for use within my program and with some slight improvements work great with no obtuse errors.
 
Share this answer
 
Comments
Graeme_Grant 1-Apr-24 9:51am    
If you were working with WPF, I have a sample of how to lazy-load branches on demand (when expanded). Look at the Google Drive - File Explorer example: Working with Newtonsoft.Json in C# & VB[^] (sample code in both C# & VB).

This is working with JSON data, however, the same principle applies.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900