Как избежать ошибки PathTooLongException?

298
07 марта 2017, 13:06

Имеется программа обрабатывающая различные файловые пути (напр.: C:\Users\J.Doe, \\Server\Share, Z:\Mapped\Drive). Для однозначной обработки файлов их пути приводятся к UNC-формату, если файл располагается не на локальном диске. Для избежания возникновения исключения PathTooLongException можно к локальным путям добавлять префикс \\?\, но в этом случае сам путь не обрабатывается, т.е. не раскрываются ссылки, относительные пути и т.д.. И насколько я понимаю, это не применимо к UNC-путям (поправьте, если не прав). Нашел статью .NET 4.6.2 and long paths on Windows 10, но как понятно из названия, сие работает только с Windows 10 и не работает с UNC, а как быть со старыми версиями ОС и файлами в сети?

Как принято обходить эту ошибку? Можно ли получить доступ к файлу по UNC пути, если он длиннее MAX_PATH?

Answer 1

К сожалению, на старых версиях Windows придётся прибегать к прямому использованию WinAPI. Примеры кода есть в этой статье, придётся написать собственный вспомогательный класс вместо File.

Я пользуюсь вот таким классом:

public static class LongPathFile
{
    [Flags]
    public enum EFileAccess : uint
    {
        GenericRead = 0x80000000,
        GenericWrite = 0x40000000,
        GenericExecute = 0x20000000,
        GenericAll = 0x10000000,
    }
    [Flags]
    public enum EFileShare : uint
    {
        None = 0x00000000,
        Read = 0x00000001,
        Write = 0x00000002,
        Delete = 0x00000004,
    }
    public enum ECreationDisposition : uint
    {
        New = 1,
        CreateAlways = 2,
        OpenExisting = 3,
        OpenAlways = 4,
        TruncateExisting = 5
    }
    [Flags]
    public enum EFileAttributes : uint
    {
        Readonly = 0x00000001,
        Hidden = 0x00000002,
        System = 0x00000004,
        Directory = 0x00000010,
        Archive = 0x00000020,
        Device = 0x00000040,
        Normal = 0x00000080,
        Temporary = 0x00000100,
        SparseFile = 0x00000200,
        ReparsePoint = 0x00000400,
        Compressed = 0x00000800,
        Offline = 0x00001000,
        NotContentIndexed = 0x00002000,
        Encrypted = 0x00004000,
        Write_Through = 0x80000000,
        Overlapped = 0x40000000,
        NoBuffering = 0x20000000,
        RandomAccess = 0x10000000,
        SequentialScan = 0x08000000,
        DeleteOnClose = 0x04000000,
        BackupSemantics = 0x02000000,
        PosixSemantics = 0x01000000,
        OpenReparsePoint = 0x00200000,
        OpenNoRecall = 0x00100000,
        FirstPipeInstance = 0x00080000
    }
    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    internal static extern SafeFileHandle CreateFile(
        string lpFileName,
        EFileAccess dwDesiredAccess,
        EFileShare dwShareMode,
        IntPtr lpSecurityAttributes,
        ECreationDisposition dwCreationDisposition,
        EFileAttributes dwFlagsAndAttributes,
        IntPtr hTemplateFile);
    public static SafeFileHandle GetCreateHandle(string path)
    {
        var handle = CreateFile(
                            path,
                            EFileAccess.GenericWrite,
                            EFileShare.None,
                            IntPtr.Zero,
                            ECreationDisposition.CreateAlways,
                            0,
                            IntPtr.Zero);
        var lastWin32Error = Marshal.GetLastWin32Error();
        if (handle.IsInvalid)
            throw new Win32Exception(lastWin32Error);
        return handle;
    }
    public static FileStream Create(string path)
    {
        return new FileStream(GetCreateHandle(path), FileAccess.Write);
    }
    public static SafeFileHandle GetReadHandle(string path)
    {
        var handle = CreateFile(
                            path,
                            EFileAccess.GenericRead,
                            EFileShare.None,
                            IntPtr.Zero,
                            ECreationDisposition.OpenExisting,
                            0,
                            IntPtr.Zero);
        var lastWin32Error = Marshal.GetLastWin32Error();
        if (handle.IsInvalid)
            throw new Win32Exception(lastWin32Error);
        return handle;
    }
    public static FileStream OpenRead(string path)
    {
        return new FileStream(GetReadHandle(path), FileAccess.Read);
    }
}

Использовать так же, как и File.Create/File.OpenRead:

using (var file = LongPathFile.Create(path))
    stream.CopyTo(file);
READ ALSO
Определить язык произвольной строки

Определить язык произвольной строки

Из вне приходит произвольная строка и нужно определить ее язык

355
Добавление выбранной пользователем ватермарки на выбранную им же картинку?

Добавление выбранной пользователем ватермарки на выбранную им же картинку?

Суть проста В picturebox загружается картинка, потом по нажатию кнопки добавляется ватермарка НО! Сейчас добавляется только указанная в пути...

223
Определение типа значения в строке из XML

Определение типа значения в строке из XML

Имеется XML документ, из него идет парсинг значений полей и далее необходимо узнать тип значения записанный в строковой переменнойДля определения...

253
Параметры query_posts

Параметры query_posts

Использую следующий код, чтобы выводить посты из custom post type

279