Twice in the last couple of weeks I have been asked about an issue with packaging an application that turned out to be related to the file path lengths in the application. This resulted in some research, which we will share here.
Paths and Long paths
File paths in the NTFS file system were originally limited to 255 characters. Furthermore, a Folder (aka Directory) could not be longer than 13 characters less, in order to allow for the directory separator and an 8.3 filename that might be added later.
All currently supported versions of the Microsoft operating system (since 1607), by default bypass this limit, and seemingly infinite path length. Oh, there probably is a limit but you won’t run into it unless you write a script to find it.
But unfortunately it turns out to be maybe you won’t have an issue. The problem is that there exists software that doesn’t know about the longer limits. This includes, unfortunately, APIs and dlls that are part of the OS. But also other applications. Programs not written in Unicode, especially Win32 programs but even some .Net Framework apps, may have problems. And sometimes even the best written app ends up using an OS supplied API that fails due to length.
Sometimes you can sneak past the limit when using software that is not long path aware by prepending the string “\\?\” in front of the file path you request. As in “\\?\C:\reallylongpath.txt”. But there is always a chance that the software is written in something in an unmanaged language like C or C++ which doesn’t allocate enough memory for the longer path and then things tend to fail or crash.
Also we’ll mention that at some point the number 255 seems to have been changed to 260 in some Microsoft documentation. We aren’t sure when or why, but that prepend string is 4 characters, isn’t it?
Microsoft MSIX Packaging Tool
The current MMPT (1.2024.405) is a program that has some limits around these ranges. The purpose of this tool is to monitor for file/registry changes for the purpose of repackaging the application into an MSIX package. The tool does appear to notice all changes regardless of path length of how it was placed on the disk.
Ultimately the tool will store those files but using a variablized relative path that may turn out to be slightly longer or shorter. For example, for program files it is one character longer but for appdata it is less.
But when processing the capture into a package, various limits come into play. We did some detailed testing around those limits.
Ultimately the tool uses the MakeAppX utility to generate the package. Part of that result is that the package contents are placed in a compressed archive (zip like format). The files are stored in the archive using relative paths like “VFS\ProgramFilesX64\..” rather than the actual path of the file (“C:\Program Files\…”). The zip format used does not impose a file length limit, but from testing we find that limits exist. From logging, we believe that the MMPT ends up dropping files prior to calling MakeAppX, probably because that component would break.
Here are some of the limits that we found.
Close of Monitoring: Directory lengths
At the close of monitoring, if any file path of a directory is way too long, the MMPT will issue a dialog and the capture is toast, as shown below. At that point your capture is toast. We didn’t measure the exact number on this one, but a 266 character path length of a folder was enough. Based on other tests, we guess the limit on the folder is maybe around 255-262.
Empty Folders
Additionally we should explain that while compressed archive formats generally support both file and directory references, Microsoft chooses to only add files to a package, and let directories be implied by the file-paths present. We see this in the MMPT when the application installer creates an empty folder. To overcome the limitation of their file only implementation, the MMPT generates a zero byte file called “MsixPackagingToolFile.txt” and adds it to this folder. The presence of this file is unlikely to matter to the application, but if the folder was somewhat near the limit, the extra 34 characters for that long filename (plus one more for the \) might put it over the MMPT limit when it gets to that point. Below is how an empty folder (named “Empty”) looks when the package is viewed in our TMEditX program.
Failing on files “reasonably” over the limit
When the MMPT goes to make the package, it appears, from the logging, that it then makes an attempt to analyze the file and then fails, then dropping the file from the package. The MMPT issues no outbound messaging to the packager to indicate this happened, and indeed the package will save “Successfully”. To see that there was a problem, after saving the package you browse the logging folder (conveniently there is a link in the popup message that claims the package saved successfully) and open a file called Log.txt. A message such as the one below indicates the issue:
Here is an example of a couple of files that were OK in the same log:
As you can see, the file in the “250” folder is find and the “251” fails. Those numbers are the length of the folder, so we add 11 characters for the file name and 1 more for the \ and we see that the relative file path limit in the package is actually 262 characters.
Keep in mind that there may be paths with special characters, such as a space in the name. Any special character needs two extra characters for the stored relative path in the archive (stored as %20) and international characters can add five extra characters.
How to fix up the package later
Missing files that are over the limit, or even folders that near the limit, is probably going to be a problem for the captured application. But even though the MMPT dropped them, as long as it successfully saved the package there is hope.
As much as I’d like to say, “always check the MMPT log file”, I know that I just don’t do that. Most apps are just fine. But when I use TMEditX, I might get a clue before testing begins.
Starting in version 5.6 of TMEditX, we display the maximum relative file path that was saved in the package. TMEditX is not subject to the limit, and normally uses the MakeMsix utility rather than MakeAppx (unless international characters are present).
- If there were a file that got dropped, we know that it was at least 263 characters long. So if the maximum in the package is above 240, there is be room for concern.
- If there was an empty folder that got dropped, we know the end of the folder name was more than 262-35 = 227. So with a 12 character folder name the concern level might be 215 in the current package as displayed by TMEditX (and there are extremely unlikely scenarios where it could be much less).
But maybe you just note it and test the package for an issue. The problem with that, of course, is you might not test that one part of the app that needed the missing file/directory.
So ultimately you probably package it again and this time check the log file.
If your package is too long, it may be possible to install to a shorter folder. Keep in mind that this means the relative VFS folder name that will be in the package, so installing to the root of C: might not help since that ends up as VFS\AppVPackageRoot.
More likely, you note the missing files in the log, and on the packaging machine make a copy of them and where they go.
Then open the package in TMEditX and you can add the missing files back in on the c.Files tab and save the package off. TMEditX avoids all of the length issues and you can have folders and files of seemingly any length.
Except for this
We ran into a customer that disabled long path support on their OS. This setting means that while TMEditX may be willing to deal with long paths, the underlying utilities that would support a (for example) 250 length file path written to a folder (like the Temp folder we extract to) will fail if the extracted length would be more than 260-ish in length.
The disable is in the Windows Registry as described in the Microsoft Documentation Maximum Path Length Limitation – Win32 apps | Microsoft Learn
HKLM\System\CurrentControlSet\Control\FileSystem LongPathsEnable DWORD = 0/1
Correction below:
The value is set to 0 in the OS by default, so someone has to turn this on to get the support. The other way to turn this on is to use a Group Policy Object (AD or Intune support) that will override the registry setting. Look for Computer Configuration > Administrative Templates > System > Filesystem > Enable Win32 long paths.
You should turn that on, please!
Recent Comments