Is there a .NET API to return the equivalent exten

2019-06-26 00:19发布

问题:

In NTFS, I can prefix a path with the \\?\ character sequence to denote that it is a path that exceeds the 260-character limit; as such, the file system will interpret the path correctly and avoid raising PathTooLongException.

(see http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx#maxpath for more information)

Is there a .NET API that will prefix my path strings with this sequence, or am I stuck writing my own?

In essence, I am looking for a method that is equivalent to the following.

static string ToExtendedPath(string path)
{
    if(Path.IsPathRooted(path))
    {
        return @"\\?\" + path;
    }

    return Path.Combine(@"\\?\", path);
}

回答1:

No, there is no .NET API that translates a given "normal" path into the extended syntax. You have to roll your own (which is trivial, by the way).

Please note: As Cody Gray and Hans Passant mentioned, the .NET framework does not support long (extended) paths. If you want to work with them, you need to use the API directly. And not all API functions support long paths either. Generally, the low-level functions do. Consult the MSDN documentation.

What I have done is write wrapper functions for the relevant API functions (e.g. CreateFile) and call those wrappers instead of the .NET file system functions.



回答2:

As, @helge-klein pointed out there is no .Net API to work around the limitation of 260 characters, that feature is totally OS dependant, some of which support a Registry based override of the 260 max_path limitation.

[Edit] DotNet 4.6.2 onwards support: System.IO.LongPath, Blog and sample

Windows 10 Creators Update has extended the kernel (and command prompt) to have MoveFileExW, but as is evident in the dotnetReferenceSource no use of the extended kernel is included in the DotNet Framework System.IO.File:

#if FEATURE_CORESYSTEM
    [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
    [ResourceExposure(ResourceScope.Machine)]
    private static extern bool MoveFileEx(String src, String dst, uint flags);

    internal static bool MoveFile(String src, String dst)
    {
        return MoveFileEx(src, dst, 2 /* MOVEFILE_COPY_ALLOWED */);
    }
#else // FEATURE_CORESYSTEM
    [DllImport(KERNEL32, SetLastError=true, CharSet=CharSet.Auto, BestFitMapping=false)]
    [ResourceExposure(ResourceScope.Machine)]
    internal static extern bool MoveFile(String src, String dst);
#endif // FEATURE_CORESYSTEM

An example LinqPad program to wrap MoveFile (others can be found on pinvoke)

void Main()
{
    //Create 3 files: in c:\temp\test\
    //testsrc0.txt, testsrc1.txt and testsrc2.txt
    //"\\?\UNC\server\share", 
    string src0File = @"\\?\UNC\127.0.0.1\c$\temp\test\testsrc0.txt";
    string dst0File = @"\\?\UNC\127.0.0.1\c$\temp\test\testdst0.txt";
    string dst0FileDotNet = @"c:\temp\test\testdst0.txt";
    string src1File = @"\\?\c:\temp\test\testsrc1.txt";
    string dst1File = @"\\?\c:\temp\test\testdst1.txt";
    string dst1FileDotNet = @"c:\temp\test\testdst1.txt";
    string src2File = @"\\?\\127.0.0.1\c$\temp\test\testsrc2.txt";
    string dst2File = @"\\?\\127.0.0.1\c$\temp\test\testdst2.txt";
    string dst2FileDotNet = @"c:\temp\test\testdst2.txt";

    MoveFileEx(src0File, dst0File, MoveFileFlags.MOVEFILE_REPLACE_EXISTING);
    System.IO.File.Exists(dst0File).Dump("File0 Exists");//FALSE
    System.IO.File.Exists(dst0FileDotNet).Dump("File0 Exists");//TRUE    

    MoveFileEx(src1File, dst1File, MoveFileFlags.MOVEFILE_REPLACE_EXISTING);
    System.IO.File.Exists(dst1File).Dump("File1 Exists");//FALSE
    System.IO.File.Exists(dst1FileDotNet).Dump("File1 Exists");//TRUE    

    MoveFileEx(src2File, dst2File, MoveFileFlags.MOVEFILE_REPLACE_EXISTING);
    System.IO.File.Exists(dst2File).Dump("File2 Exists");//FALSE
    System.IO.File.Exists(dst2FileDotNet).Dump("File2 Exists");//FALSE - as missing UNC keyword
    System.Runtime.InteropServices.Marshal.GetLastWin32Error().Dump("ERROR:");//3 == ERROR_PATH_NOT_FOUND
}

[Flags]
enum MoveFileFlags
{
    MOVEFILE_REPLACE_EXISTING = 0x00000001,
    MOVEFILE_COPY_ALLOWED = 0x00000002,
    MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
    MOVEFILE_WRITE_THROUGH = 0x00000008,
    MOVEFILE_CREATE_HARDLINK = 0x00000010,
    MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
}

// Define other methods and classes here
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);

Paths with a drive and UNC path need the keyword "UNC". \?\UNC\