Long Paths and .NET

If you’ve been following me on Twitter the last little while you might have noticed a few tweets bemoaning long paths in .NET code.  This hasn’t been something new to me, but I decided to attempt to do something about it in the last little while.

Supporting files and directories with a long path is fairly easy with Windows. Unfortunately, other aspects of Windows haven’t supported long paths in their entirely. The file system (NTFS), for example, supports long paths quite well; but other things like Command Prompt and Explorer don’t. This makes it hard to entirely support long paths in any application, let alone in .NET.x

What is “long path”?

Some of you (and I envy you) probably don’t know what a “long path” is.  It’s different from “long names”—which is a filename long than the archaic 8.3 filenames and support spaces.  Windows, for the longest time (and still does, for that matter) supported paths (drive specifier, directory, subdirectories, filename, extension) *up to* about 260 chars.  There are multiple ways to get around that (which actually create long paths and lead you down a path of anguish) but to support paths larger that 260 chars you have to use the long path support in Windows (and NTFS).  This support (which is essentially Unicode paths) supports up to 32,000 characters in a path.  (don’t talk to me if you reach **that** limit).  This still has its limits in that a *directory* is still limited to 256 characters (which is really a feature of the driver for that file system—but NTFS, last time I checked, supports up to to 256 characters per directory (which stems from 260: 256 chars for a directory, 3 chars for the drive specifier and one char for the null).  But, 256 chars for a directory name ought to be enough for everyone :).

Supporting a Long Path in .NET

This has been a bit tricky in .NET. Several attempts like longpaths.codeplex.com (a more up to date version has made its way into .NET in classes like LongPath LongPathFile and LongPathDirectory. But, these libraries do not seem to support the entire original API (Path, File, Directory) and not all file-related APSs (including FileInfo, DirectoryInfo, FileSystemInfo).

Often times long path support is an after thought. Usually after you’ve released something and someone logs bug (e.g. “When I use a path like c:\users\300 chars removed\Document.docx your software gives me an error”. You can likely support long paths with the above-mentioned libraries, but you end up having to scrub your existing code that works with regular paths and re-design it to suit these new APIs (causing full re-tests, potential new errors, potential regressions, etc.).

So, I’m announcing an open-source library that I’ve created: LongPath (or Pri.LongPath on nuget).

LongPath is a .NET 4.5 library that attempts to make dealing with long paths *much* easier.  LongPath originally started as a fork of LongPaths on Codeplex; but after initial usage it was clear that much more work was involved to better support long paths. So, I drastically expanded the API scope to include FileInfo, DirectoryInfo, FileSystemInfo to get 100% API coverage supporting long paths. (with one caveat: Directory.SetCurrentDirectory, Windows does not support long paths for a current directory).

LongPaths allows your code to support long paths by providing a drop-in replacement for the following System.IO types: FileInfo, DirectoryInfo, FileSystemInfo, FileInfo, DirectoryInfo, FileSystemInfo. You simply reference the Pri.LongPath types you need and you don’t need to change your code.

Obviously to replace only 6 types in a namespaces (System.IO) and not the rest is problematic because you’re going to need to use some of those other types (FileNotFoundException, FileMode, etc.)–which means referencing System.IO and re-introducing the original 6 types back into your scope. I feft that not having to modify your code was the greater of the two evils. Resolving this conflict is easily solved through aliases.  So, if you’re used to one of the System.IO classes FileInfo, DirectoryInfo, FileSystemInfo, FileInfo, DirectoryInfo, FileSystemInfo, you simply add the following aliases to your C# file and go on about your business.

I think there’s currently 300 test that exercise the library pretty well; but if you find any issues, let me know via an issue on github, or a push request with a failing test.  Or, fix it and send me a push request with the fix and a new passing test.

I’m going to add support for prior versions of .NET (and future then they’re available) in the next little while (and over time); but if you’re interested in contributing, see the issues list on github, or add a new issue, or fork and start making changes.

Enjoy!

  • ferventcoder

    http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath Just adding the reference for those that want to go look this up.

  • http://www.thomaslevesque.com/ Thomas Levesque

    Hi Peter,

    Your lib looks great, I will probably use it soon! However it’s not a perfect drop-in replacement for System.IO: even though the API surface is the same, the behavior differs in the way it handles error cases. For instance, if a file doesn’t exist, System.IO.File.GetLastWriteTime gives a dummy date (1601/01/01), whereas Pri.LongPath.File.GetLastWriteTime throws an exception. I think your approach makes more sense, but people who use your lib should be aware of the difference. Also, I noticed that FileInfo.ToString returns the full path in System.IO, whereas Pri.LongPath returns just the name. In this case I think it would be better to remain consistent with System.IO.

    For the record, AlphaFS (https://alphafs.codeplex.com/) uses a similar approach (drop-in replacement), but it doesn’t seem to be maintained anymore.

    • peterritchie

      A great point Thomas. There could be more parity there. I didn’t have the time to entirely grok the nuances of every method in the existing API. To a certain degree, it can never be a “perfect drop-in replacement” because you *want* different behaviour (i.e. much less PathTooLongExceptions :) ). Given that it’s supposed to be a drop-in replacement and GetLastWriteTime isn’t affected by long-paths, I think it’s better for it to return a dummy date. Also, FileInfo.ToString should return the same thing as System.IO. Thanks for pointing those out. Let me know if your find anything else.

      I briefly looked at alphaFS, but it does more than just long-path support, so I didn’t look too closely at it.

  • julodnik

    Hi Peter

    I have build your library in my project (ET-Explorer) a Windows Explorer alternative which I have been working on over the last years. It is a fairly complex application (more than one would expect when it comes to something as basic as a file explorer). If you want to check it here is the url : http://sourceforge.net/projects/etexplorer/

    Anyway, I just wanted to thank you for this library. I was able to replace the standard NET Library with your library in less than ten minutes. Being able to do that for a file explorer says already something about the quality of your work and the level of compatibility you have accomplished.

    I just had to fix a small issue. Your library tries to prefix also networks path and that does not work. To be able to just drop your library without changing any code I had to disable the prefixing for network paths.

    Otherwise it is cool being able to access now paths with my explorer, which even the standard windows explorer or the command prompt is not able to access.

    Many thanks again.

    • peterritchie

      @julodnik, I’m glad you like and find it useful. That’s exactly why I made it available :)