Edit: 06/17/2016: I’ve gotten a few comments that for some reason mention “Long Path Tool”. I’m not clear why that is the case. Any posts mentioning Long Path tool as an “Alternative” or because you were “encountering the same problem” will be treated as spam. In the meantime, I’ll continue to use the Search Program I wrote almost a decade ago in Visual Basic 6 to delete files that are beyond the MAX_PATH limit (it uses my BCFile library which has the ability to support long path names).
Starting in Windows 10 Build 14352, a new Group Policy has appeared, “Enable NTFS Long Paths”. This setting is found under Administrative Templates\System\File System\NTFS:
This setting relates to the somewhat ubiquitous 260 character path limit. But what exact is the limit, how did it come about, and how has it persisted to this day in such a way that, as the setting itself requires, programs need to explicitly declare that they can handle them?
First, the initial origin. This limitation actually dates to MS-DOS, and originates with Interrupt 21h with 47h in the high byte of the accumulator register. This function was defined as “returns the path description without the drive letter and the initial backslash”. the 256 byte path excluded the drive letter and backslash (as noted) and didn’t use a null terminator. Adding the path and appropriate null terminator brings the maximum buffersize for a full directory path specification to 260 bytes.
Windows 3.1, and Windows 3.x more or less stuck with these same limitations, being mostly built over top of MS-DOS. With the introduction of the Unicode API, it was decided that Windows ought to allow for longer paths, particularly as File Systems such as NTFS were being developed that did away with many of those DOS-Derived limitations. So with those NT functions, the Unicode version of file functions could accept a \\?\ prefix at the front of the path string. This specified to the function to enable support for longer path names, up to the NTFS maximum of 32768. The requirement for \\?\ was added to allow older programs that weren’t compatible to continue to function; since they would not send the \\?\ prefix, they would continue to work as before.
However, as it happened, it turned out that this hasn’t been a particularly good solution. It wasn’t well popularized that File API calls should include such prefixes, and furthermore Microsoft’s own, built-in software didn’t even use it properly. Programs such as Windows Explorer don’t send the prefix string; and the .NET File API doesn’t support Unicode long path names either.
To demonstrate this difference, I’ve constructed a crude example in C#. It builds a long path that is 500+ characters long, then attempts to create it using the .NET API, and then using CreateDirectoryW:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace longnametest { class Program { //Native methods. [DllImport("kernel32.dll",CharSet=CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool CreateDirectory(string lpPathName, IntPtr lpSecurityAttributes); static void Main(string[] args) { String ProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); //attempt to create a 500+ long path. while(ProfilePath.Length < 500) { ProfilePath = Path.Combine(ProfilePath, "long"); } Console.WriteLine("Constructed Path of length " + ProfilePath.Length); Console.WriteLine("Attempting to create path using Directory.Create..."); try { Directory.CreateDirectory(ProfilePath); } catch(Exception exx) { Console.WriteLine("Exception attempting to use Directory.Create:" + exx.ToString()); } Console.WriteLine("Using CreateDirectoryW..."); String[] PathParts = ProfilePath.Split(Path.DirectorySeparatorChar); String CreateDir = Path.Combine(PathParts[0] + Path.DirectorySeparatorChar, PathParts[1],PathParts[2],PathParts[3]); for(int i=3;i<PathParts.Length;i++) { if(!CreateDirectory("\\\\?\\" + CreateDir, IntPtr.Zero)) { int lastError = Marshal.GetLastWin32Error(); if (lastError != 183) { Console.WriteLine("CreateDirectory Failure..." + lastError); break; } } CreateDir = Path.Combine(CreateDir, PathParts[i]); } Console.ReadKey(); } } } |
This gives output similar to the following. Note that this is the case on earlier Windows versions as well, going back to XP:
1 2 3 4 5 6 7 8 9 10 11 |
Constructed Path of length 501 Attempting to create path using Directory.Create... Exception attempting to use Directory.Create:System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters. at System.IO.PathHelper.GetFullPathName() at System.IO.Path.LegacyNormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths) at System.IO.Path.NormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths) at System.IO.Path.GetFullPathInternal(String path) at System.IO.Directory.InternalCreateDirectoryHelper(String path, Boolean checkHost) at System.IO.Directory.CreateDirectory(String path) at longnametest.Program.Main(String[] args) in c:\users\michael\documents\visual studio 2015\Projects\longnametest\longnametest\Program.cs:line 32 Using CreateDirectoryW... |
What happened? the .NET Function failed when it saw the path would be too long and gave up. CreateDirectoryW however was able to create the full path length. I was able to verify it’s length because neither Command Prompt nor Windows Explorer are able to enter it. Interestingly, on Windows 10, this is the case regardless of if the Group Policy is Enabled or not.
Now, my current suspicion is that the feature will enable the functionality of \\?\ from those unicode functions without \\?\ being specified. In this case, this likely means that the internal .NET functions will still not function with longer path names, as the .NET File API is currently stopping it from going any further; it is checking the path length and going “Nope, won’t work, I give up” and throwing an exception. If my assertion is correct, however, it means that CreateDirectoryW will work as it does now if I remove the \\?\ prefix text if I add the appropriate manifest declaration to the program.
Which, naturally, led me to the next question- what exactly needs to be added to the manifest? First, I dumped the manifest from explorer.exe, hoping it perhaps declared support (which I was skeptical of given that I wasn’t able to browse the folders that were created as part of the test code). The Windows 10 Explorer.exe from build 14352 current has the following information from SysInternals sigcheck:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
c:\windows\explorer.exe: Verified: Signed Signing date: n/a Publisher: Microsoft Windows Description: Windows Explorer Product: Microsoft® Windows® Operating System Prod version: 10.0.14352.1002 File version: 10.0.14352.1002 (rs1_release.160522-1930) MachineType: 64-bit Manifest <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!-- Copyright (c) Microsoft Corporation --> <!-- This is the manifest file only for Explorer. It only differs from windowsshell.manifest in the <dpiAware> tag. --> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity name="Microsoft.Windows.Shell.explorer" processorArchitecture="amd64" version="5.1.0.0" type="win32"/> <description>Windows Shell</description> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"/> </requestedPrivileges> </security> </trustInfo> <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">Explorer</dpiAware> </windowsSettings> </application> </assembly> |
Well, no clues there. Dang. I tried paving my own roadway here, but can’t identify what xml namespace the app manifest would refer to nor the actual setting; I tried <longPaths> “ntfsLongPaths” and a few other permutations; however it may be the case that the xml namespace has not been published and this feature simply hasn’t been documented. The group policy setting may even be a placeholder in this build, given built-in Windows applications currently possess no support for it’s manifested capabilities. (Note that with the setting enabled, without a manifest, using CreateDirectoryW without the prefix results in an error once it goes beyond 260 characters).
This is a topic I will certainly revisit once more information on how we can utilize this feature comes to light. However, as it is, it doesn’t seem terribly useful. The big use here will be if Windows’ built-in software is written to support the policy setting. At any rate, I intend to update this posting when new information is available about this feature and how it can be used by developers. as well as it’s effect on the software.
Update 1:
Windows 10 does NOT display a correct error message when deleting a folder created in the manner above; When I attempted to delete the folder created above that is longer than MAX_PATH, I receive the following error dialog:
It claims that the source file names are larger than is supported by the file system. There are a few issues with that sort of error. I’m not copying files, so there is no “Source” or “Destination”; more importantly, the file system is NTFS and the fact that it currently contains the files I’m trying to delete seems to be presentable as evidence that the file system does, in fact, fully support the file path length.
These are the sorts of misleading error messages that have plagued Windows because of this MAX_PATH limitation for decades. Whether this group policy- which isn’t enabled by default to begin with- is some sort of lead-in to a wider solution to this problem and to solve misleading and straight-up incorrect error messages like the above will be interesting to see.
Update 2
Allegedly, the setting in the manifest is “longPathAware”. However, I’ve yet to identify different behaviour when adding such a node to a manifest. The example program above is unable to use CreateDirectoryW and construct the 600+ Character path; it still requires the special prefix string. I’m looking through “Strings” output to verify that longPathAware is likely the manifest name.
I’ve found a RtlIsLongPathAwareProcessByManifest function as well as a direct reference to the string longPathAware which seems to indicate that is the correct manifest element. I found it references the schema http://schemas.microsoft.com/SMI/2016/WindowsSettings, which I wasn’t using. However adding that xmlns didn’t seem to work. So The manifest element is correct and the xmlns seems to be correct but I cannot make the feature do anything.
Have something to say about this post? Comment!