Public Enum PathType
File
Directory
End Enum
Public Shared Function GetRealPath(ByVal file As IO.FileInfo) As String
Return GetRealPath(file.FullName, PathType.File)
End Function
Public Shared Function GetRealPath(ByVal folder As IO.DirectoryInfo) As String
Return GetRealPath(folder.FullName, PathType.Directory)
End Function
Public Shared Function GetRealPath(ByVal filePath As String, ByVal pathType As PathType) As String
Dim FullPath As String = String.Empty
If filePath Is Nothing OrElse String.IsNullOrEmpty(filePath) Then
Throw New ArgumentNullException("No path specified")
Else
If filePath.IndexOfAny(IO.Path.GetInvalidPathChars) >= 0 Then
Throw New ArgumentException("The specified path '" & filePath & "' is invalid")
Else
If pathType = PathType.File Then
Try
Dim TempFile As New IO.FileInfo(filePath)
If TempFile.Name.IndexOfAny(Path.GetInvalidFileNameChars) >= 0 Then
Throw New ArgumentException("The specified file name '" & filePath & "' is invalid")
End If
TempFile = Nothing
Catch ex As Exception
Throw New ArgumentException("The specified file name '" & filePath & "' is invalid", ex)
End Try
End If
' The path should not contain any invalid characters. Start trying to populate the FullPath variable.
If IO.Path.IsPathRooted(filePath) Then
FullPath = filePath
Else
Try
FullPath = IO.Path.GetFullPath(filePath)
Catch ex As Exception
Throw New ArgumentException("The specified path '" & filePath & "' is invalid", ex)
End Try
End If
If Not FullPath.StartsWith("\\") Then
Dim PathRoot As String = IO.Path.GetPathRoot(FullPath)
If PathRoot Is Nothing OrElse String.IsNullOrEmpty(PathRoot) Then
FullPath = String.Empty
Throw New ArgumentException("The specified path '" & filePath & "' is invalid")
Else
If Not IO.Directory.GetLogicalDrives.Contains(PathRoot) Then
FullPath = String.Empty
Throw New ArgumentException("The specified path '" & filePath & "' is invalid. Drive '" & PathRoot & "' does not exist.")
Else
Dim CurrentDrive As New System.IO.DriveInfo(PathRoot)
If CurrentDrive.DriveType = DriveType.Network Then
Using HKCU As Microsoft.Win32.RegistryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Network\" & FullPath(0))
If Not HKCU Is Nothing Then
FullPath = HKCU.GetValue("RemotePath").ToString() & FullPath.Remove(0, 2).ToString()
End If
End Using
ElseIf Not CurrentDrive.DriveType = DriveType.NoRootDirectory AndAlso Not CurrentDrive.DriveType = DriveType.Unknown Then
Dim SubstPath As String = String.Empty
If IsSubstPath(FullPath, SubstPath) Then
FullPath = SubstPath
End If
Else
FullPath = String.Empty
Throw New ArgumentException("The specified path '" & filePath & "' is invalid. Drive '" & CurrentDrive.Name & "' does not exist.")
End If
End If
End If
End If
End If
End If
Return FullPath
End Function
<DllImport("kernel32.dll", SetLastError:=True)>
Private Shared Function QueryDosDevice(ByVal lpDeviceName As String, ByVal lpTargetPath As System.Text.StringBuilder, ByVal ucchMax As Integer) As UInteger
End Function
Private Shared Function IsSubstPath(ByVal pathToTest As String, <Out> ByRef realPath As String) As Boolean
Dim PathInformation As System.Text.StringBuilder = New System.Text.StringBuilder(250)
Dim DriveLetter As String = Nothing
Dim WinApiResult As UInteger = 0
realPath = Nothing
Try
' Get the drive letter of the path
DriveLetter = IO.Path.GetPathRoot(pathToTest).Replace("\\""\", "")
Catch ex As ArgumentException
Return False
End Try
WinApiResult = QueryDosDevice(DriveLetter, PathInformation, 250)
If WinApiResult = 0 Then
' For debugging
Dim LastWinError As Integer = Marshal.GetLastWin32Error()
Return False
End If
' If drive is SUBST'ed, the result will be in the format of "\??\C:\RealPath\".
If PathInformation.ToString().StartsWith("\??\") Then
Dim RealRoot As String = PathInformation.ToString().Remove(0, 4)
RealRoot += If(PathInformation.ToString().EndsWith("\"), "", "\")
realPath = IO.Path.Combine(RealRoot, pathToTest.Replace(IO.Path.GetPathRoot(pathToTest), ""))
Return True
End If
realPath = pathToTest
Return False
End Function
Throws exception ("Illegal characters in path") from inside the GetRealPath() method when attempting to call IO.Path.GetFullPath() on the String value (line 46 in the method's code posted above) due to illegal characters in the path (?)
I may have to add a parameter to define whether or not I want to allow relative paths to be expanded, or possibly check for an appropriate character sequence (./, /, .., etc.) at the start of the string before "approving" the return value. Otherwise, pretty much any string value passed in could potentially result in a "legitimate" path.
##EDIT
I've successfully tested the SUBST condition on my local machine (see how my ignorance and "over-confidence" caused me some grief in my question on SO). It looks like this is all working correctly, even though, in the end, I may choose to make a few minor modifications, including:
- I may have to add a parameter to define whether or not I want to allow relative paths to be expanded, and/or possibly check for an appropriate character sequence (
./,/,.., etc.) at the start of the string before "approving" the return value. Otherwise, pretty much any string value passed in could potentially result in a "legitimate" path. - I've been strongly considering making the "workhorse" overload (
GetRealPath(String, PathType)) aPrivatemethod (along with thePathTypeEnum) to allow the validation intrinsic to theIO.FileInfoandIO.DirectoryInfoobjects help prevent some of the "unexpected" or "unintended" results from allowing any randomStringinput, such as in the last example.