135 lines
4.6 KiB
C#
135 lines
4.6 KiB
C#
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
|
|
namespace FileManager
|
|
{
|
|
public class LongPath
|
|
{
|
|
//https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
|
|
|
|
public const int MaxDirectoryLength = MaxPathLength - 13;
|
|
public const int MaxPathLength = short.MaxValue;
|
|
public const int MaxFilenameLength = 255;
|
|
|
|
private const int MAX_PATH = 260;
|
|
private const string LONG_PATH_PREFIX = @"\\?\";
|
|
|
|
public string Path { get; init; }
|
|
public override string ToString() => Path;
|
|
|
|
internal static readonly PlatformID PlatformID = Environment.OSVersion.Platform;
|
|
|
|
|
|
public static implicit operator LongPath(string path)
|
|
{
|
|
if (PlatformID is PlatformID.Unix) return new LongPath { Path = path };
|
|
|
|
if (path is null) return null;
|
|
|
|
//File I/O functions in the Windows API convert "/" to "\" as part of converting
|
|
//the name to an NT-style name, except when using the "\\?\" prefix
|
|
path = path.Replace(System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar);
|
|
|
|
if (path.StartsWith(LONG_PATH_PREFIX))
|
|
return new LongPath { Path = path };
|
|
else if ((path.Length > 2 && path[1] == ':') || path.StartsWith(@"UNC\"))
|
|
return new LongPath { Path = LONG_PATH_PREFIX + path };
|
|
else if (path.StartsWith(@"\\"))
|
|
//The "\\?\" prefix can also be used with paths constructed according to the
|
|
//universal naming convention (UNC). To specify such a path using UNC, use
|
|
//the "\\?\UNC\" prefix.
|
|
return new LongPath { Path = LONG_PATH_PREFIX + @"UNC\" + path.Substring(2) };
|
|
else
|
|
{
|
|
//These prefixes are not used as part of the path itself. They indicate that
|
|
//the path should be passed to the system with minimal modification, which
|
|
//means that you cannot use forward slashes to represent path separators, or
|
|
//a period to represent the current directory, or double dots to represent the
|
|
//parent directory. Because you cannot use the "\\?\" prefix with a relative
|
|
//path, relative paths are always limited to a total of MAX_PATH characters.
|
|
if (path.Length > MAX_PATH)
|
|
throw new System.IO.PathTooLongException();
|
|
return new LongPath { Path = path };
|
|
}
|
|
}
|
|
|
|
public static implicit operator string(LongPath path) => path?.Path;
|
|
|
|
[JsonIgnore]
|
|
public string ShortPathName
|
|
{
|
|
get
|
|
{
|
|
if (PlatformID is PlatformID.Unix) return Path;
|
|
|
|
//Short Path names are useful for navigating to the file in windows explorer,
|
|
//which will not recognize paths longer than MAX_PATH. Short path names are not
|
|
//always enabled on every volume. So to check if a volume enables short path
|
|
//names (aka 8dot3 names), run the following command from an elevated command
|
|
//prompt:
|
|
//
|
|
// fsutil 8dot3name query c:
|
|
//
|
|
//It will say:
|
|
//
|
|
// "Based on the above settings, 8dot3 name creation is [enabled/disabled] on c:"
|
|
//
|
|
//To enable short names on a volume on the system, run the following command
|
|
//from an elevated command prompt:
|
|
//
|
|
// fsutil 8dot3name set c: 0
|
|
//
|
|
//or for all volumes on the system:
|
|
//
|
|
// fsutil 8dot3name set 0
|
|
//
|
|
//Note that after enabling 8dot3 names on a volume, they will only be available
|
|
//for newly-created entries in ther file system. Existing entries made while
|
|
//8dot3 names were disabled will not be reachable by short paths.
|
|
|
|
if (Path is null) return null;
|
|
|
|
StringBuilder shortPathBuffer = new(MaxPathLength);
|
|
GetShortPathName(Path, shortPathBuffer, MaxPathLength);
|
|
return shortPathBuffer.ToString();
|
|
}
|
|
}
|
|
|
|
[JsonIgnore]
|
|
public string LongPathName
|
|
{
|
|
get
|
|
{
|
|
if (PlatformID is PlatformID.Unix) return Path;
|
|
if (Path is null) return null;
|
|
|
|
StringBuilder longPathBuffer = new(MaxPathLength);
|
|
GetLongPathName(Path, longPathBuffer, MaxPathLength);
|
|
return longPathBuffer.ToString();
|
|
}
|
|
}
|
|
|
|
[JsonIgnore]
|
|
public string PathWithoutPrefix
|
|
{
|
|
get
|
|
{
|
|
if (PlatformID is PlatformID.Unix) return Path;
|
|
return
|
|
Path?.StartsWith(LONG_PATH_PREFIX) == true ? Path.Remove(0, LONG_PATH_PREFIX.Length)
|
|
:Path;
|
|
}
|
|
}
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
|
private static extern int GetShortPathName([MarshalAs(UnmanagedType.LPWStr)] string path, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder shortPath, int shortPathLength);
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
|
private static extern int GetLongPathName([MarshalAs(UnmanagedType.LPWStr)] string lpszShortPath, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpszLongPath, int cchBuffer);
|
|
|
|
}
|
|
}
|