15

I have set Enable Win32 Long Paths in the Local Group Policy Editor to Enabled and restarted the computer.

And here's the code:

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

I'm getting the error:

System.IO.DirectoryNotFoundException: 'Could not find a part of the path 'C:\Users...\Desktop\zzzzzzzzzz...

(Which is actually a strange error message.)

app.config already has:

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

More info (probably not important)

I tried adding as mentioned in this post and elsewhere (though as pointed out in the comments it's not needed when using .net 4.7) in app.config under configuration:

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

Still same error.

If I only use one zzzzzz... it creates it on the desktop with no error.

I'm using VS2017, Windows 10. I tried Winforms and WPF.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
ispiro
  • 26,556
  • 38
  • 136
  • 291
  • 1
    this is not error of 4.7 This is limit of file path available in Windows. By default it hardcoded as 260 characters. – eocron Jul 03 '17 at 15:06
  • 4
    @eocron not with `UseLegacyPathHandling` set to false. – NtFreX Jul 03 '17 at 15:07
  • 1
    You do realize that the UseLegacyPathHandling switch is for applications that target **below** 4.6.2 as per the linked article, right? – Camilo Terevinto Jul 03 '17 at 15:09
  • 8
    You'll have to convince [the OS that you know what you are doing](https://blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/net-4-6-2-and-long-paths-on-windows-10/). – Hans Passant Jul 03 '17 at 15:10
  • @CamiloTerevinto No. I didn't realize this. Though the question still stands. (It might have been answered by Hans Passant, though.) – ispiro Jul 03 '17 at 15:11
  • @HansPassant Thanks. I'm looking into it, and assume that it's the correct answer, so you can transform your comment into an answer. (And by the way, this means that we can't really ship apps using long paths to the general public. What a huge waste...) – ispiro Jul 03 '17 at 15:13
  • @ispiro "Long paths aren’t enabled by default yet.", let's hope yet means some next OS update. But you could also toggle that policy from code – Camilo Terevinto Jul 03 '17 at 15:15
  • @CamiloTerevinto I guess I didn't get to that part yet (though I don't know if I'd like to do that). Where is that stated or how can I do that? – ispiro Jul 03 '17 at 15:17
  • [GPO for long path support](https://www.saotn.org/ntfs-long-paths-windows-server-2016-gpo/) - though that article specifically references WS2016, it may be the same in Win10. – stuartd Jul 03 '17 at 15:19
  • @ispiro not sure if you can do it with c#, but I've done similar tasks with PowerShell, look here: https://technet.microsoft.com/itpro/powershell/windows/grouppolicy/grouppolicy – Camilo Terevinto Jul 03 '17 at 15:21
  • @HansPassant I just set `Enable Win32 Long Paths` in the Local Group Policy Editor to `Enabled`. I'm still seeing the same error. Any ideas? – ispiro Jul 03 '17 at 15:23
  • @CamiloTerevinto Thanks. – ispiro Jul 03 '17 at 15:23
  • @ispiro do you have the supportedRuntime element set to 4.6.2/4.7 as suggested in the article? – Camilo Terevinto Jul 03 '17 at 15:25
  • 1
    @CamiloTerevinto Yes. ``. – ispiro Jul 03 '17 at 15:26
  • @ispiro `4.0` isn't quite the same as `version=".NETFramework,Version=v4.6.2` (or so I'd guess, not really sure) – Camilo Terevinto Jul 03 '17 at 15:27
  • 1
    @HansPassant I now rebooted as well, and tried using `DirectoryInfo` instead, to create the Directory. Still no go. – ispiro Jul 03 '17 at 15:49
  • @ispiro, i tried your code on vm and successfully created the nested folder structure. so whatever the problem is its not related to the instructions and code in your post. i installed the 4.7 .net framework and the developer pack on clean vm with vs2017, enabled the setting in group policy and added to the config the required line. maybe the problem lies in your environment setup. – igorc Jul 07 '17 at 06:19
  • 1
    I just tried your code (with long path enabled) and moved the createdirectory in the loop, just to see when the error occurs: It happens after the first iteration (so it creates one directory and no subdirectories). – Max Play Jul 08 '17 at 16:46
  • @jasondinAlt Thanks. – ispiro Jul 09 '17 at 20:47
  • Related: http://dotnetrocks.com/?show=1320 – Hans Kesting Jul 11 '17 at 12:48

3 Answers3

19

The Anniversary Update (RS1) has a bug that allow long paths to work without the manifest. For any updated Windows you must add the Application Manifest File item to your project. Otherwise it will not work.

<application xmlns="urn:schemas-microsoft-com:asm.v3">
  <windowsSettings>
    <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
  </windowsSettings>
</application>
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Alexrgs
  • 831
  • 8
  • 20
  • 2
    Thanks. +50 (+25). If you have a source for this, that would be nice too. But in any case - this solved the problem. This explains why some people seem not to have this problem, and I do. Because I'm running on Windows 10 Creators update. – ispiro Jul 11 '17 at 17:12
  • what about ASP .Net applications? – Mike W Mar 04 '22 at 14:22
2

This might not answer your question but give you a tip for a workaround. I tested your snippet with mono 4.5 under Ubuntu Linux and works like a charm, but in Windows the story might be a little bit different. Here, the one to blame seems to be the .NET Framework itself, regarding this article and this other article, does not support long paths.

Therefore, the solution as @Anastasiosyal suggest in this StackOverflow answer is to rely on the Windows Api itself. There are two ways: direct bypassing or Api call.

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

Api call (code is not mine, got it from @Anastasiosyal answer):

// 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);
    }
}

In addition, I advise you to use Path.Combine instead of path + "\\" + subpath.

Kyordhel
  • 157
  • 3
  • 13
  • Thanks. But .net _does_ support long paths since .net 4.6.2. As for `Path.Combine`, a) This was the quickest for the example. b) Though I generally agree that using a method is better - `Path.Combine` is an exception. See https://stackoverflow.com/questions/53102/why-does-path-combine-not-properly-concatenate-filenames-that-start-with-path-di#comment8540140_53102 . – ispiro Jul 11 '17 at 13:15
2

I have an experience:

1) in desktop application (.NET 4.7) you not need nothing more, then use path name with prefix @"\?\ (don't need manifest, set UseLegacyPathHandling in app.confing) and all works

2) in web application you have to set this:

  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!");
    }
  }

I hope the help you!