B4J Question URI for networked drives on Windows

Didier99

Member
Licensed User
One of the things that drives (pun intended) me nuts with Windows is the use of drive letters as shortcuts to network path.
I understand the convenience within the scope of a single user, but since there is no enforcement that all users on a network use the same drive letter for a given resource, it's very hard to reliably share resource locations across users.
The point is that when I select a file, I would like to get the absolute network path as URI instead of a relative path to a drive letter.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I think that your best option is to run "net use" with jShell:
B4X:
Private Sub Button1_Click
    Wait For (NetUse) Complete (StdOut As String)
End Sub

Private Sub NetUse As ResumableSub
    Dim s As Shell
    s.Initialize("s", "net", Array("use"))
    s.Run(60000)
    Wait For (s) s_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    Log($"ExitCode: ${ExitCode}, StdOut: ${StdOut}, StdErr: ${StdOut}"$)
    Return StdOut
End Sub

I don't have any mapped network drive here, so can't help you with parsing. Post the output if need assistance with this.
 
Upvote 0

teddybear

Well-Known Member
Licensed User
Supplementary the URI of parsing the StdOut
B4X:
Private Sub Button1_Click
    Wait For (NetUse) Complete (StdOut As String)
    Dim Matcher1 As Matcher
    Matcher1 = Regex.Matcher("\\\\[A-Za-z0-9-]+\\", StdOut)
    Do While Matcher1.Find
        Log("URI: " & Matcher1.Match)
    Loop
End Sub
 
Upvote 0

Didier99

Member
Licensed User
Thank you both, this will do the job very nicely!
It's interesting because I thought that we could somehow make the file chooser output URI instead of a file path with drive letter, but this solution is much more flexible in my opinion.
 
Upvote 0

Didier99

Member
Licensed User
I have packaged the examples above into a short project that replaces a drive letter with a URI when available.
I also test for the presence of the selected file on a secondary drive (like a D: drive that is not networked)

At turn on, we create a map of the accessible network drives and use that to convert a Windows path with network drive letter into a URI.
If you map a new network drive while the program is running, you are out of luck :) until you call PopulateDriveLetterMap again.

replaceDriveLetterWithURI:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Private DriveLetterMap As Map ' contains a map of the network locations mapped to a drive letter
    Type returnType( code As Int, msg As String )
    Private fc As FileChooser
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    DriveLetterMap.Initialize
    fc.Initialize
    PopulateDriveLetterMap
End Sub

Sub Button1_Click
    Private filename As String
    fc.InitialDirectory = "C:\"
    fc.InitialFileName = ""
    Dim filename As String = fc.ShowOpen( MainForm )
    Log( "Windows filename: " & filename )
    Log( "URI filename: " & GetFilenameAsURI( filename ))
End Sub

Sub GetFilenameAsURI( filename As String ) As String
    If filename.Length > 0 Then
        Dim rc As returnType = replaceDriveLetterWithURI( filename )
        If rc.code > -1 Then
            filename = rc.msg
        Else
            Log( "Drive letter not found" )
        End If
    End If
    Return filename
End Sub ' GetFilenameAsURI()

Sub PopulateDriveLetterMap
    Wait For (NetUse) Complete (StdOut As String)
    Dim r() As String = Regex.Split( CRLF, StdOut )
    For Each s As String In r
        If s.IndexOf(":") > -1 Then
            Log( s )
            Dim driveLetter As String = s.SubString2( s.IndexOf(":")-1, s.IndexOf(":")+1 )
            DriveLetterMap.Put( driveLetter, s.SubString( s.IndexOf(":")+1 ).Trim )
        End If
    Next
End Sub ' PopulateDriveLetterMap

Private Sub NetUse As ResumableSub
    Dim s As Shell ' requires the jShell library
    s.Initialize("s", "net", Array("use"))
    s.Run(60000)
    Wait For (s) s_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
    Return StdOut
End Sub ' NetUse

' replace drive letter with URI by collecting the drive letter mapping at runtime
Sub replaceDriveLetterWithURI( path As String ) As returnType
    Dim e As returnType
    e.Initialize
    ' set defaults if drive letter not found
    e.code = 0 ' path does not contain a drive letter
    e.msg = path ' return path as-is
    If path.IndexOf( ":" ) > -1 Then
        Dim driveLetter As String =    path.SubString2( path.IndexOf(":")-1, path.IndexOf(":")+1 )
        If driveLetter <> "C:" Then
            If DriveLetterMap.ContainsKey( driveLetter ) Then
                e.code = 1 ' path contained a drive letter
                e.msg = DriveLetterMap.get( driveLetter ) & path.SubString( 2 )
            Else If File.Exists( path.SubString2( 0, path.LastIndexOf( "\" )), path.SubString( path.LastIndexOf( "\" )+1) ) = False Then
                ' path includes a drive letter that is not C and not a mapped network drive and not secondary local storage
                e.code = -1 ' path contained a drive letter that is not mapped to a network resource on this machine
                e.msg = "Drive letter " & driveLetter & " not found on this machine"
            End If
        End If
    End If
    Return e
End Sub ' replaceDriveLetterWithURI()
 
Upvote 0
Top