E' vero che la ricorsione non è mai necessaria, ma questo mi sembra proprio un caso tipicamente ricorsivo.

Nell'esempio di sotto ho utilizzato una tabella che, in origine mi serviva per creare un menu.
La stessa tabella mi serve in questo caso per crearmi la struttura di Directory-files su disco.

le voci principali della tabella sono
id_menu, tuo id_cat
text, tuo nome_cat
id_parent, tuo parent_id
type, D per directory, F per file

codice:
Option Strict On

Partial Class prove_a
    Inherits System.Web.UI.Page

    Protected Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
        CreaMenu()
    End Sub

    Private Sub CreaMenu()
        'estraggo tutte le voci, file e directory
        Dim dt As DataTable = ora.GetDataTable(gl.StringaConnessione, String.Format("select * from menu where id_modulo = {0} ", 2))

        'considero solo i file e le directory di primo livello
        Dim rows As DataRow() = dt.Select("id_parent is null")
        For Each row As DataRow In rows
            Dim id_menu As Integer = CInt(row("id_menu"))
            Dim text As String = l.NullToString(row("text")).Replace("\"c, "_"c).Replace("/"c, "_"c).Replace("|"c, "_"c).Replace("<"c, "_"c).Replace(">"c, "_"c).Replace(":"c, "_"c)
            Dim type As String = l.NullToString(row("type"))

            'creo tutta la struttura in c:\tmp per non sporcarmi il computer
            text = String.Format("c:\tmp\{0}", text)

            'se directory
            If String.Equals(type, "D", StringComparison.OrdinalIgnoreCase) Then
                Directory.CreateDirectory(text)
                PopolaMenu(dt, id_menu, text)
            Else
                'se file
                File.CreateText(text)
            End If

        Next

    End Sub


    Protected Sub PopolaMenu(dt As DataTable, id_parent As Integer, textParent As String)
        Dim rows As DataRow() = dt.Select(String.Format("id_parent = {0}", id_parent))
        For Each row As DataRow In rows
            Dim id_menu As Integer = CInt(row("id_menu"))
            Dim text As String = l.NullToString(row("text")).Replace("\"c, "_"c).Replace("/"c, "_"c).Replace("|"c, "_"c).Replace("<"c, "_"c).Replace(">"c, "_"c).Replace(":"c, "_"c)
            Dim type As String = l.NullToString(row("type"))

            text = String.Format("{0}\{1}", textParent, text)

            If String.Equals(type, "D", StringComparison.OrdinalIgnoreCase) Then
                Directory.CreateDirectory(text)

                'richiamo ricorsivamente PopolaMenu
                PopolaMenu(dt, id_menu, text)
            Else
                File.CreateText(text)
            End If

        Next
    End Sub


End Class