Ошибка использования длинных путей в.net 4.7

Я поставил Enable Win32 Long Paths в редакторе локальной групповой политики, чтобы Enabled и перезагрузил компьютер.

И вот код:

string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
for (int i = 0; i < 10; i++)
    path += "\\" + new string('z', 200);
Directory.CreateDirectory(path);

Я получаю ошибку:

System.IO.DirectoryNotFoundException: "Не удалось найти часть пути" C: \ Users... \ Desktop \ zzzzzzzzzz...

(Что на самом деле странное сообщение об ошибке.)

app.config уже имеет:

<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" />

Больше информации (вероятно, не важно)

Я попытался добавить, как упоминалось в этом посте и в других местах (хотя, как указано в комментариях, это не нужно при использовании.net 4.7) в app.config под configuration:

<runtime>
  <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" />
</runtime>

Все та же ошибка.

Если я использую только один zzzzzz... он создает его на рабочем столе без ошибок.

Я использую VS2017, Windows 10. Я пробовал Winforms и WPF.

2 ответа

Решение

В Anniversary Update (RS1) есть ошибка, позволяющая работать с длинными путями без манифеста. Для любых обновленных окон вы должны добавить элемент "Файл манифеста приложения" в ваш проект. Иначе это не сработает.

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
  </windowsSettings>
</application>

У меня есть опыт:

1) в настольном приложении (.NET 4.7) вам больше ничего не нужно, затем используйте путь с префиксом @"\?\ (Не требует манифеста, установите UseLegacyPathHandling в app.confing) и все работает

2) в веб-приложении вы должны установить это:

  bool legacyPaths;
  if (AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out legacyPaths) && legacyPaths)
  {
    var switchType = Type.GetType("System.AppContextSwitches"); 
    if (switchType != null)
    {
      AppContext.SetSwitch("Switch.System.IO.UseLegacyPathHandling", false);   
      var legacyField = switchType.GetField("_useLegacyPathHandling", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
      legacyField?.SetValue(null, (Int32)0); 
      AppContext.TryGetSwitch("Switch.System.IO.UseLegacyPathHandling", out legacyPaths);
      Assert.IsFalse(legacyPaths, "Long pathnames are not supported!");
    }
  }

Я надеюсь, что помощь вам!

Это может не ответить на ваш вопрос, но даст вам совет для обхода. Я протестировал ваш фрагмент с mono 4.5 под Ubuntu Linux и работает как шарм, но в Windows история может быть немного другой. Здесь, по-видимому, виноват сам.NET Framework, что касается этой и другой статей, не поддерживает длинные пути.

Следовательно, решение, предложенное @Anastasiosyal в ответе Stackru, заключается в использовании самого Windows Api. Есть два способа: прямой обход или вызов API.

Directory.CreateDirectory(@"\\?\" + veryLongPath);

Вызов API (код не мой, получил ответ @Anastasiosyal):

// This code snippet is provided under the Microsoft Permissive License.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

[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 void TestCreateAndWrite(string fileName) {

    string formattedName = @"\\?\" + fileName;
    // Create a file with generic write access
    SafeFileHandle fileHandle = CreateFile(formattedName,
        EFileAccess.GenericWrite, EFileShare.None, IntPtr.Zero,
        ECreationDisposition.CreateAlways, 0, IntPtr.Zero);

    // Check for errors
    int lastWin32Error = Marshal.GetLastWin32Error();
    if (fileHandle.IsInvalid) {
        throw new System.ComponentModel.Win32Exception(lastWin32Error);
    }

    // Pass the file handle to FileStream. FileStream will close the
    // handle
    using (FileStream fs = new FileStream(fileHandle,
                                    FileAccess.Write)) {
        fs.WriteByte(80);
        fs.WriteByte(81);
        fs.WriteByte(83);
        fs.WriteByte(84);
    }
}

Кроме того, советую использовать Path.Combine вместо path + "\\" + subpath,

Другие вопросы по тегам