DirectoryNotFoundException when using long paths in .NET 4.7
Asked Answered
H

3

16

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.

Hedgcock answered 3/7, 2017 at 15:3 Comment(20)
this is not error of 4.7 This is limit of file path available in Windows. By default it hardcoded as 260 characters.Chassis
@Chassis not with UseLegacyPathHandling set to false.Ectomorph
You do realize that the UseLegacyPathHandling switch is for applications that target below 4.6.2 as per the linked article, right?Voussoir
You'll have to convince the OS that you know what you are doing.Lobbyism
@CamiloTerevinto No. I didn't realize this. Though the question still stands. (It might have been answered by Hans Passant, though.)Hedgcock
@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...)Hedgcock
@Hedgcock "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 codeVoussoir
@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?Hedgcock
GPO for long path support - though that article specifically references WS2016, it may be the same in Win10.Keystone
@Hedgcock not sure if you can do it with c#, but I've done similar tasks with PowerShell, look here: technet.microsoft.com/itpro/powershell/windows/grouppolicy/…Voussoir
@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?Hedgcock
@CamiloTerevinto Thanks.Hedgcock
@Hedgcock do you have the supportedRuntime element set to 4.6.2/4.7 as suggested in the article?Voussoir
@CamiloTerevinto Yes. <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7" />.Hedgcock
@Hedgcock 4.0 isn't quite the same as version=".NETFramework,Version=v4.6.2 (or so I'd guess, not really sure)Voussoir
@HansPassant I now rebooted as well, and tried using DirectoryInfo instead, to create the Directory. Still no go.Hedgcock
@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.Hammond
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).Chateau
@jasondinAlt Thanks.Hedgcock
Related: dotnetrocks.com/?show=1320Cassiopeia
C
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>
Contraband answered 11/7, 2017 at 14:46 Comment(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.Hedgcock
what about ASP .Net applications?Morganica
R
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.

Reel answered 11/7, 2017 at 11:11 Comment(1)
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 #53602 .Hedgcock
C
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!

Cheery answered 17/3, 2019 at 10:29 Comment(1)
Did you mean prefix @"\\?\" (note two slashes in the beginning of string)Weyermann

© 2022 - 2024 — McMap. All rights reserved.