More on new .NET path handling

In my prior post I talked briefly about the new path handling in .NET 4.6.2. In the latest drop (14367) PowerShell opts into both of the new behaviors so you can easily play around with the changes.

There are two basic changes that can be controlled via two compatibility switches. The changes both originated in CoreFX so you can get a more detailed look if you so choose. I'm going to talk about these this week on On .NET, so bring your questions.

Normalization Changes

The first change is that we now try to defer to the OS to normalize paths. Trying to emulate what the OS does led to some discrepancies- one notable one being that you couldn't do anything with directories that end in a space. You can create directories with a space at the end from the CMD prompt, but they were impossible to do anything with in .NET. Refer back to my post on normalization to see a bit more about what is/isn't possible in Windows.

The second change is that we allow better access to DOS device paths. This includes both the normalized (\\.\) and non-normalized (\\?\) syntaxes. These were poorly documented and subsequently poorly understood. Handling was spotty and inconsistent. Getting at paths that were possible using non-normalized syntax (such as \\?\C:\foo.) used to be impossible in .NET.

Both of the DOS device path syntaxes skip most character validity checks. Why? Those checks were wrong and essentially impossible to get correct. Named pipes, for example, can use many characters you can't use in a "normal" NTFS path (and do, Nvidia creates a pipe like this on my machine). This sort of validation is better left to the OS and the various drivers involved, so we do.

The extended DOS device syntax will only work for core APIs that live in mscorlib if you're in full trust. FileIOPermissions historically is the class responsible for kicking \\?\ paths and still is. Comparing extended paths is too costly to do in FileIOPermissions (partially because paths don't have to exist, which makes the logic crazy). For this release the internal usages in mscorlib have a special out when we're running full trust. This should cover the vast majority of usages. I'm looking into expanding this to all usages of FileIOPermissions in full trust in a future .NET. (Note that there are no such limitations for CoreFX as there is no limited trust model.)

Normalization changes are controlled via the UseLegacyPathHandling switch, which is off by default if you target 4.6.2 or higher (the semantics here are part of the AppContext design- you opt into backwardly compatible behavior on newer Framework versions).

Long Path Changes

The key driver in re-evaluating the normalization was the need to support long paths (paths over MAX_PATH, or 260 characters). The old logic didn't scale and prevented usage of the current "long path" solution on Windows, extended DOS device paths (\\?\).

In 4.6.2 we will no longer throw PathTooLongException if we see a path that is >= MAX_PATH. If the OS doesn't like it we'll surface whatever the OS returns as an error (which may be PathTooLong), but we won't second guess what the OS will do. This enables you to use extended long paths and, if the OS would let you, regular long paths. *cough* 🙂

(Note that in many cases the OS will return DirectoryNotFound for paths that are too long. The primary case is with creating files. We don't have a way to programmatically check whether paths are actually too long so we can't translate DirectoryNotFound into PathTooLong. We're looking at updating the exception text in the future to say it "may also be caused by a path that is too long" where such a thing is possible.)

In CoreFX, .NET will put the extended syntax on for you when calling Windows APIs. This allows you to use long paths without any extra thought or new OS support (if it should ever arrive *cough* *cough*). This implicit handling isn't done in 4.6.2 as it was to costly to fit in the schedule (it is a much more difficult task to get this implicit support in the desktop code as opposed to CoreFX).

The long path changes are controlled via the BlockLongPaths switch, which again is off by default if you target 4.6.2 or higher.

Config File Setting

If you target 4.6.2 this isn't necessary, but if you want to enable the behavior for existing code, here is config file snippet you need to use.

<?xml version="1.0" encoding="utf-8" ?>
    <AppContextSwitchOverrides value="Switch.System.IO.UseLegacyPathHandling=false;Switch.System.IO.BlockLongPaths=false" />
Comments (20)
  1. GreenCat says:

    Does long path capable is only Win10?

    1. JeremyKuhne says:

      These .NET features are part of 4.6.2 in general- it just happens to be in the current public Windows 10 builds so it’s easy to play with the changes there.

  2. Sune Foldager says:

    If CoreFX puts extended path syntax on (presumably converting C:\foo.txt to \\?\C:\foo.txt) what about normalization, which is normally skipped when using \\?\? E.g. how is “..\foo.txt” handled; by normalizing it first or explicitly calling GetFullPath?

    1. JeremyKuhne says:

      Every .NET API call hits GetFullPath first. This was historically the case (partially due to need to check permissions)- which made implementing this plausible.

  3. Julien Jacobs says:

    Just 2 words: GREAT and THANKS!

    This was simply the most wanted feature in the community for years. Thank you for making it happened!

    1. JeremyKuhne says:

      You’re welcome! There is more to do, keep the feedback coming!

  4. It’s not working for me. My app config is:

    Code is:

    var fileName = @”\\?\d:\temp\_12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\test.txt”;
    try {
    File.AppendAllText(fileName, “Hallo”);
    } catch (Exception ex) {
    //=> System.Exception {System.IO.PathTooLongException}

    System.Exception {System.IO.PathTooLongException} is thrown.

    I have Windows Version 1607 Build 14393.10 German.
    I don’t habe the options described here: The new policy is just not existing…

    Any help would be great!

    1. App config got removed from post so here the important part:
      supportedRuntime version=”v4.0″ sku=”.NETFramework,Version=v4.6.2″

      1. OK, I found the solution. After installing the Developer Pack of the Framework and switching the Target to Framework 4.6.2 it works like a charm. Also without the prefix \\.\ or \\?\ And the Folder created can be navigated in the Windows Explorer too!!!! (Still Windows Explorer will not allow you to create longer paths manually. But once you are in a logger Path there are no problems)

        Well: Word and Excel still have smaller problems with the long names, but the files are opened and this is perfect.

        Thank you so much!!!

        1. JeremyKuhne says:

          There is some magic in Windows Explorer that has existed for some time- it will utilize the 8.3 names to try and get a path under MAX_PATH. You’ll see this if you select the full path after navigating in. 8.3 generation has to be turned on, of course, and the 8.3 path has to be under MAX_PATH. 8.3 is the default for the boot drive at least- I’m not sure what the exact rules are for other drives, but I *think* it is off now by default for all other drives.

  5. Z says:

    What’s the earliest .Net version that the above config works with for long file share paths like \\share\…? When I tried with .Net 4.5.1, I got a FileNotFoundException on a file with a 260 character path.

    1. JeremyKuhne says:

      4.6.2 or higher needs to be installed.

  6. Ben says:

    How do you get long paths to work for unit tests? I keep having an issue where the UseLegacyPathHandling flag is killing my tests. My application works fine, I just need to get our tests working with long paths.

    1. JeremyKuhne says:

      Your test runner needs the right manifest/.config.

  7. Michael says:

    So, to be clear: If I target 4.6.2, I don’t need to use \\?\ for long paths and this new feature will work on pre-Windows 10 OSes too?

    1. JeremyKuhne says:

      \\?\ will work on any OS if you have 4.6.2 or higher installed and are targeting 4.6.2 or are using the right config switches.

  8. Sunny says:

    After making that config changes, I am getting System.IO.DirectoryNotFoundException instead of PathTooLongException exception.

    1. JeremyKuhne says:

      Yes, that can happen. I’ve updated the text to be clearer about this. Unfortunately CreateFile translates errors from normalization (such as > MAX_PATH) to path not found. I don’t have a way to see the original error, baring getting a change/fix from Windows I’m looking at changing the exception text to specify that the error may also be caused when paths are too long.

  9. Pooja Parmar says:

    This Solution works only if we have config file. Now in our case we need to add this setting in our class library so is it possible or not.?

    1. JeremyKuhne says:

      You can’t unfortunately. You’d have to update the .config for the launching .exe. While there is an API for programmatically making these settings you can’t do so after they’ve been accessed. It is nearly impossible to set these particular flags before something else uses Path.GetFullPath for the first time. 🙂

      Changing the behavior is problematic as it would introduce compatibililty breaks. Code is undoubtably written assuming that the flags do not change, given that is the way they currently work. That said, always feel free to open issues on for .NET features/functionality you are interested in having. We track .NET 4.x requests there as well.

Comments are closed.

Skip to main content