Answered Path.GetFullPath - can't give 2nd parameter

robertb_NZ

Well-known member
Joined
May 11, 2010
Messages
146
Location
Auckland, New Zealand
Programming Experience
10+
I have a reference
JSPG1I.json
that should be resolved relative to another file. It shoujld be simple to get this using Path.GetFullPath(File, Path): -
VB.NET:
Dim File1 As String = "C:\tutorials\TstSQL\JSPG1.json"
Dim Path1 As String = Path.GetDirectoryName(File1)
Dim File2 As String = "JSPG1.json"
Dim Path2 = Path.GetFullPath(File2, Path1)
But I get this error message: -
Severity Code Description Project File Line Suppression State Program Sequence Number
Error BC30057 Too many arguments to 'Public Shared Overloads Function GetFullPath(path As String) As String'.

I am using Visual Studio targeting Net Framework 4.0, and I see that the version of Path.GetFullPath for Net Framework (any version) only supports the first argument. I think that the code above would work if I changed to Net Core. Is this a minor change, or should I be concerned about this? If it is NOT a minor change, what is the best way of achieving what I want while staying within Net Framework? My current best thought is
VB.NET:
Dim Save = Environment.CurrentDirectory
Dim Path2 = Path.GetFullPath(File2)
Environment.Currentdirectory = save

EDIT: Environment.CurrentDirectory DOESN'T SEEM TO BE AVAILABLE IN Net Framework 4

BTW, the File2 value could be any valid relative path. Preceding logic already removes URL's (start with http...) and absolute references (start with x:).
 
Last edited:
To use .NET Core, you would have to create a new project and choose .NET Core at the outset. You could then simply import existing items from your old project and most, if not all, should probably work as is. If your application is Windows Forms or WPF, the designer is not yet ready for full release, so it may not be a perfect experience.

If you want to stick with .NET Framework, you could implement the same functionality as .NET Core for yourself. Here's the source code for that method:
C#:
public static string GetFullPath(string path, string basePath)
{
    if (path == null)
        throw new ArgumentNullException(nameof(path));

    if (basePath == null)
        throw new ArgumentNullException(nameof(basePath));

    if (!IsPathFullyQualified(basePath))
        throw new ArgumentException(SR.Arg_BasePathNotFullyQualified, nameof(basePath));

    if (basePath.Contains('\0') || path.Contains('\0'))
        throw new ArgumentException(SR.Argument_InvalidPathChars);

    if (IsPathFullyQualified(path))
        return GetFullPath(path);

    if (PathInternal.IsEffectivelyEmpty(path))
        return basePath;

    int length = path.Length;
    string combinedPath = null;

    if ((length >= 1 && PathInternal.IsDirectorySeparator(path[0])))
    {
        // Path is current drive rooted i.e. starts with \:
        // "\Foo" and "C:\Bar" => "C:\Foo"
        // "\Foo" and "\\?\C:\Bar" => "\\?\C:\Foo"
        combinedPath = Join(GetPathRoot(basePath.AsSpan()), path.AsSpan(1)); // Cut the separator to ensure we don't end up with two separators when joining with the root.
    }
    else if (length >= 2 && PathInternal.IsValidDriveChar(path[0]) && path[1] == PathInternal.VolumeSeparatorChar)
    {
        // Drive relative paths
        Debug.Assert(length == 2 || !PathInternal.IsDirectorySeparator(path[2]));

        if (GetVolumeName(path).EqualsOrdinal(GetVolumeName(basePath)))
        {
            // Matching root
            // "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
            // "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
            combinedPath = Join(basePath, path.AsSpan(2));
        }
        else
        {
            // No matching root, root to specified drive
            // "D:Foo" and "C:\Bar" => "D:Foo"
            // "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo"
            combinedPath = !PathInternal.IsDevice(basePath)
                ? path.Insert(2, @"\")
                : length == 2
                    ? JoinInternal(basePath.AsSpan(0, 4), path, @"\")
                    : JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), @"\", path.AsSpan(2));
        }
    }
    else
    {
        // "Simple" relative path
        // "Foo" and "C:\Bar" => "C:\Bar\Foo"
        // "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
        combinedPath = JoinInternal(basePath, path);
    }

    // Device paths are normalized by definition, so passing something of this format (i.e. \\?\C:\.\tmp, \\.\C:\foo)
    // to Windows APIs won't do anything by design. Additionally, GetFullPathName() in Windows doesn't root
    // them properly. As such we need to manually remove segments and not use GetFullPath().

    return PathInternal.IsDevice(combinedPath)
        ? PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath))
        : GetFullPath(combinedPath);
}
and here's a VB equivalent, courtesy of Instant VB:
VB.NET:
Imports Microsoft.VisualBasic

Public Shared Function GetFullPath(ByVal path As String, ByVal basePath As String) As String
    If path Is Nothing Then
        Throw New ArgumentNullException(NameOf(path))
    End If

    If basePath Is Nothing Then
        Throw New ArgumentNullException(NameOf(basePath))
    End If

    If Not IsPathFullyQualified(basePath) Then
        Throw New ArgumentException(SR.Arg_BasePathNotFullyQualified, NameOf(basePath))
    End If

    If basePath.Contains(ControlChars.NullChar) OrElse path.Contains(ControlChars.NullChar) Then
        Throw New ArgumentException(SR.Argument_InvalidPathChars)
    End If

    If IsPathFullyQualified(path) Then
        Return GetFullPath(path)
    End If

    If PathInternal.IsEffectivelyEmpty(path) Then
        Return basePath
    End If

    Dim length As Integer = path.Length
    Dim combinedPath As String = Nothing

    If (length >= 1 AndAlso PathInternal.IsDirectorySeparator(path.Chars(0))) Then
        ' Path is current drive rooted i.e. starts with \:
        ' "\Foo" and "C:\Bar" => "C:\Foo"
        ' "\Foo" and "\\?\C:\Bar" => "\\?\C:\Foo"
        combinedPath = Join(GetPathRoot(basePath.AsSpan()), path.AsSpan(1)) ' Cut the separator to ensure we don't end up with two separators when joining with the root.
    ElseIf length >= 2 AndAlso PathInternal.IsValidDriveChar(path.Chars(0)) AndAlso path.Chars(1) = PathInternal.VolumeSeparatorChar Then
        ' Drive relative paths
        Debug.Assert(length = 2 OrElse Not PathInternal.IsDirectorySeparator(path.Chars(2)))

        If GetVolumeName(path).EqualsOrdinal(GetVolumeName(basePath)) Then
            ' Matching root
            ' "C:Foo" and "C:\Bar" => "C:\Bar\Foo"
            ' "C:Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
            combinedPath = Join(basePath, path.AsSpan(2))
        Else
            ' No matching root, root to specified drive
            ' "D:Foo" and "C:\Bar" => "D:Foo"
            ' "D:Foo" and "\\?\C:\Bar" => "\\?\D:\Foo"
            combinedPath = If(Not PathInternal.IsDevice(basePath), path.Insert(2, "\"), If(length = 2, JoinInternal(basePath.AsSpan(0, 4), path, "\"), JoinInternal(basePath.AsSpan(0, 4), path.AsSpan(0, 2), "\", path.AsSpan(2))))
        End If
    Else
        ' "Simple" relative path
        ' "Foo" and "C:\Bar" => "C:\Bar\Foo"
        ' "Foo" and "\\?\C:\Bar" => "\\?\C:\Bar\Foo"
        combinedPath = JoinInternal(basePath, path)
    End If

    ' Device paths are normalized by definition, so passing something of this format (i.e. \\?\C:\.\tmp, \\.\C:\foo)
    ' to Windows APIs won't do anything by design. Additionally, GetFullPathName() in Windows doesn't root
    ' them properly. As such we need to manually remove segments and not use GetFullPath().

    Return If(PathInternal.IsDevice(combinedPath), PathInternal.RemoveRelativeSegments(combinedPath, PathInternal.GetRootLength(combinedPath)), GetFullPath(combinedPath))
End Function
You'd need to look into all that internal stuff but I'll leave that to you, if you want to go that way.

By the way, Environment.CurrentDirectory is definitely available in .NET 4.0.
 
Last edited:
I've always used Path.Combine Method (System.IO) with first argument a folder path and second argument a file path (which can be relative).
VB.NET:
Dim Path2 = IO.Path.Combine(Path1, File2) '= C:\tutorials\TstSQL\JSPG1.json
 
Thank you JohnH, that's a much simpler solution than jmcilhinney's solution, which required Net Core, as there were many references to Net Core classes. This Stack Overflow page starts to unpick some of the changes I would have had to make to use it, but then your answer was posted giving me a much simpler approach. I put the logic into a function so that I could deal with references starting with "..\"
VB.NET:
    Function Combine(path1, File2) As String
        While Microsoft.VisualBasic.Left(File2, 3) = "..\"
            path1 = Path.GetDirectoryName(path1)
            File2 = Mid(File2, 4)
        End While
        Return Path.Combine(path1, File2)
    End Function
I'm researching whether, for my situation, I need to also deal with "../", and whether there are any other situations ("~", "?") that I might need to consider. For now I don't, and I can get on with the current development.
 
I'm researching whether, for my situation, I need to also deal with "../", and whether there are any other situations ("~", "?") that I might need to consider. For now I don't, and I can get on with the current development.
In your OP you said this:
the File2 value could be any valid relative path
Apparently you lied. ;)
 
If for example File2 was "..\something\JSPG1.json" then Path.Combine result would be "C:\tutorials\TstSQL\..\something\JSPG1.json". You can then use FileInfo.FullName to translate the relativity to "C:\tutorials\something\JSPG1.json".
VB.NET:
Path2 = (New IO.FileInfo(Path2)).FullName
 
Back
Top