Существует ли.NET API для возврата эквивалентной строки расширенного пути данного пути?
В NTFS я могу поставить префикс пути с помощью \\?\
последовательность символов для обозначения того, что это путь, который превышает ограничение в 260 символов; как таковая, файловая система будет правильно интерпретировать путь и избегать PathTooLongException
,
(см. http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx для получения дополнительной информации)
Существует ли.NET API, который будет префиксить мои пути к этой последовательности, или я застрял в написании своего собственного?
По сути, я ищу метод, который эквивалентен следующему.
static string ToExtendedPath(string path)
{
if(Path.IsPathRooted(path))
{
return @"\\?\" + path;
}
return Path.Combine(@"\\?\", path);
}
2 ответа
Нет,.NET API не переводит данный "нормальный" путь в расширенный синтаксис. Вы должны бросить свой собственный (кстати, тривиально).
Обратите внимание: как упоминали Коди Грей и Ханс Пассант, платформа.NET не поддерживает длинные (расширенные) пути. Если вы хотите работать с ними, вам нужно использовать API напрямую. И не все функции API поддерживают длинные пути. Как правило, функции низкого уровня делают. Обратитесь к документации MSDN.
Что я сделал, так это написал функции-оболочки для соответствующих функций API (например, CreateFile) и вызвал эти оболочки вместо функций файловой системы.NET.
Как отметил @helge-klein, нет.Net API для обхода ограничения в 260 символов, эта функция полностью зависит от ОС, некоторые из которых поддерживают переопределение ограничения 260 max_path на основе реестра.
[Редактировать] Поддержка DotNet 4.6.2 и далее: System.IO.LongPath, Блог и пример
Обновление Creators для Windows 10 расширило ядро (и командную строку) до MoveFileEx W, но, как видно из dotnetReferenceSource, в 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
Пример программы LinqPad для переноса MoveFile (другие можно найти в 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);
Пути с диском и UNC-путь требуют ключевого слова "UNC". \?\UNC\