How to avoid System.IO.PathTooLongException?

C#.Net

C# Problem Overview


We constantly run into this problem...

Example:

if I have a file that I want to copy it into an another directory or UNC share and if the length of the path exceeds 248 (if I am not mistaken), then it throws PathTooLongException. Is there any workaround to this problem?

PS: Is there any registry setting to set this path to a longer char set?

C# Solutions


Solution 1 - C#

As described in Jeremy Kuhne's blog, .NET Framework 4.6.2 removes the MAX_PATH limitation where possible, without breaking backwards compatibility.

Solution 2 - C#

Try This : Delimon.Win32.I​O Library (V4.0) This Libarary is written on .NET Framework 4.0

Delimon.Win32.IO replaces basic file functions of System.IO and supports File & Folder names up to up to 32,767 Characters.

https://gallery.technet.microsoft.com/DelimonWin32IO-Library-V40-7ff6b16c

This Library is written specifically to overcome the limitation of the .NET Framework to use long Path & File Names. With this Library you can programmatically browse, access, write, delete, etc Files and Folders that are not accessible by the System.IO namespace.Library

Usage

  1. First add a reference to the Delimon.Win32.IO.dll to your project (Browse to the Delimon.Win32.IO.dll file)

  2. In your Code File add "using Delimon.Win32.IO"

  3. Use normal File & Directory Objects as if you are working with System.IO

Solution 3 - C#

This has been discussed in depth by the BCL team, see the blog entries

In essence there is no way to do this within .Net code and stick to the BCL. Too many functions rely on being able to canonicalize the path name (which immediately triggers the use of functions expecting MAX_PATH to be obeyed).

You could wrap all the win32 functions that support the "\\?" syntax, with these you would be able to implement a suite of long path aware functionality but this would be cumbersome.

Since a vast number of tools (including explorer[1]) cannot handle long path names it is inadvisable to go down this route unless you are happy that all interaction with the resulting file system goes through your library (or the limited number of tools that are built to handle it like robocopy)

In answer to your specific need I would investigate whether the use of robocopy directly would be sufficient to perform this task.

[1] Vista has ways to mitigate the issue with some fancy renaming under the hood but this is fragile at best)

Solution 4 - C#

Only 1 workaround that I've seen on this one... this might be helpful

http://www.codeproject.com/KB/files/LongFileNames.aspx

Solution 5 - C#

The problem is with the ANSI versions of the Windows APIs. One solution that needs to be tested carefully is to force the use of Unicode versions of the Windows API. This can be done by prepending "\\?\" to the path being queried.

Great information, including work arounds can be found in the following blog posts from Microsoft's Base Class Library (BCL) Team titled "Long Paths in .NET":

Solution 6 - C#

Solution 7 - C#

My Drive-Mapping solution works fine and stable using „NetWorkDrive.cs“ and „NetWorkUNCPath.cs“ which are listed below.

Test example:

if (srcFileName.Length > 260)
{
   string directoryName = srcFileName.Substring(0, srcFileName.LastIndexOf('\\'));
 
   var uncName = GetUNCPath(srcFileName.Substring(0, 2)) + directoryName.Substring(2);

   using (NetWorkDrive nDrive = new NetWorkDrive(uncName))
   {
     drvFileName = nDrive.FullDriveLetter + Path.GetFileName(sourceFileName)
     File.Copy(drvFileName, destinationFileName, true);
   }
}
else
{
   File.Copy(srcFileName, destinationFileName, true);
}

NetWorkDrive.cs souce code:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace SeekCopySupportTool.Business
{

public class NetWorkDrive : IDisposable
{
    #region private fields
    private string m_DriveLetter = string.Empty;
    private string m_FullDriveLetter = string.Empty;
    private bool m_Disposed = false;
    //this list specifies the drive-letters, whitch will be used to map networkfolders
    private string[] possibleDriveLetters = new string[] 
    { 
        "G:\\", 
        "H:\\", 
        "I:\\", 
        "J:\\", 
        "K:\\", 
        "L:\\", 
        "M:\\", 
        "N:\\", 
        "O:\\", 
        "P:\\", 
        "Q:\\", 
        "R:\\", 
        "S:\\", 
        "T:\\", 
        "U:\\", 
        "V:\\", 
        "W:\\", 
        "X:\\", 
        "Y:\\", 
        "Z:\\" 
    };
    #endregion

    #region public properties
    public string DriveLetter
    {
        get { return m_DriveLetter; }
        set { m_DriveLetter = value; }
    }
    public string FullDriveLetter
    {
        get { return m_FullDriveLetter; }
        set { m_FullDriveLetter = value; }
    }
    #endregion

    #region .ctor
    public NetWorkDrive(string folderPath)
    {
        m_FullDriveLetter = MapFolderAsNetworkDrive(folderPath);
        if (string.IsNullOrEmpty(m_FullDriveLetter))
        {
            throw new Exception("no free valid drive-letter found");
        }
        m_DriveLetter = m_FullDriveLetter.Substring(0,2);
    }
    #endregion

    #region private methods
    /// maps a given folder to a free drive-letter (f:\)
    /// <param name="folderPath">the folder to map</param>
    /// <returns>the drive letter in this format: "(letter):\" -> "f:\"</returns>
    /// <exception cref="Win32Exception">if the connect returns an error</exception>
    private string MapFolderAsNetworkDrive(string folderPath)
    {
        string result = GetFreeDriveLetter();
        NETRESOURCE myNetResource = new NETRESOURCE();
        myNetResource.dwScope = ResourceScope.RESOURCE_GLOBALNET;
        myNetResource.dwType = ResourceType.RESOURCETYPE_ANY;
        myNetResource.dwDisplayType = ResourceDisplayType.RESOURCEDISPLAYTYPE_SERVER;
        myNetResource.dwUsage = ResourceUsage.RESOURCEUSAGE_CONNECTABLE;
        myNetResource.lpLocalName = result.Substring(0,2);
        myNetResource.lpRemoteName = folderPath;
        myNetResource.lpProvider = null;
        int errorcode = WNetAddConnection2(myNetResource, null, null, 0);
        if(errorcode != 0)
        {
            throw new Win32Exception(errorcode);
        }
        return result;
    }
    private void DisconnectNetworkDrive()
    {
        int CONNECT_UPDATE_PROFILE = 0x1;
        int errorcode = WNetCancelConnection2(m_DriveLetter, CONNECT_UPDATE_PROFILE, true);
        if (errorcode != 0)
        {
            throw new Win32Exception(errorcode);
        }
    }

    private string GetFreeDriveLetter()
    {
        //first get the existing driveletters
        const int size = 512;
        char[] buffer = new char[size];
        uint code = GetLogicalDriveStrings(size, buffer);
        if (code == 0)
        {                
            return "";
        }
        List<string> list = new List<string>();
        int start = 0;
        for (int i = 0; i < code; ++i)
        {
            if (buffer[i] == 0)
            {
                string s = new string(buffer, start, i - start);
                list.Add(s);
                start = i + 1;
            }
        }            
        foreach (string s in possibleDriveLetters)
        {                
            if (!list.Contains(s))
            {
                return s;
            }
        }
        return null;
    }        
    #endregion

    #region dll imports
    /// <summary>
    /// to connect to a networksource
    /// </summary>
    /// <param name="netResource"></param>
    /// <param name="password">null the function uses the current default password associated with the user specified by the username parameter ("" the function does not use a password)</param>
    /// <param name="username">null the function uses the default user name (The user context for the process provides the default user name)</param>
    /// <param name="flags"></param>
    /// <returns></returns>
    [DllImport("mpr.dll")]
    //public static extern int WNetAddConnection2(ref NETRESOURCE netResource, string password, string username, int flags);
    private static extern int WNetAddConnection2([In] NETRESOURCE netResource, string password, string username, int flags);
    /// <summary>
    /// to disconnect the networksource
    /// </summary>
    /// <param name="lpName"></param>
    /// <param name="dwFlags"></param>
    /// <param name="bForce"></param>
    /// <returns></returns>
    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string lpName, Int32 dwFlags, bool bForce);
    /// <param name="nBufferLength"></param>
    /// <param name="lpBuffer"></param>
    /// <returns></returns>
    [DllImport("kernel32.dll")]
    private static extern uint GetLogicalDriveStrings(uint nBufferLength, [Out] char[] lpBuffer);
    #endregion

    #region enums/structs
    /// <example>
    /// NETRESOURCE myNetResource = new NETRESOURCE();
    /// myNetResource.dwScope = 2;
    /// myNetResource.dwType = 1;
    /// myNetResource.dwDisplayType = 3;
    /// myNetResource.dwUsage = 1;
    /// myNetResource.LocalName = "z:";
    /// myNetResource.RemoteName = @"\servername\sharename";
    /// myNetResource.Provider = null;
    /// </example>
    [StructLayout(LayoutKind.Sequential)]
    public class NETRESOURCE
    {
        public ResourceScope dwScope = 0;
        public ResourceType dwType = 0;
        public ResourceDisplayType dwDisplayType = 0;
        public ResourceUsage dwUsage = 0;
        public string lpLocalName = null;
        public string lpRemoteName = null;
        public string lpComment = null;
        public string lpProvider = null;
    };
    public enum ResourceScope : int
    {
        RESOURCE_CONNECTED = 1,
        RESOURCE_GLOBALNET,
        RESOURCE_REMEMBERED,
        RESOURCE_RECENT,
        RESOURCE_CONTEXT
    };
    public enum ResourceType : int
    {
        RESOURCETYPE_ANY,
        RESOURCETYPE_DISK,
        RESOURCETYPE_PRINT,
        RESOURCETYPE_RESERVED
    };
    public enum ResourceUsage
    {
        RESOURCEUSAGE_CONNECTABLE = 0x00000001,
        RESOURCEUSAGE_CONTAINER = 0x00000002,
        RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
        RESOURCEUSAGE_SIBLING = 0x00000008,
        RESOURCEUSAGE_ATTACHED = 0x00000010,
        RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
    };
    public enum ResourceDisplayType
    {
        RESOURCEDISPLAYTYPE_GENERIC,
        RESOURCEDISPLAYTYPE_DOMAIN,
        RESOURCEDISPLAYTYPE_SERVER,
        RESOURCEDISPLAYTYPE_SHARE,
        RESOURCEDISPLAYTYPE_FILE,
        RESOURCEDISPLAYTYPE_GROUP,
        RESOURCEDISPLAYTYPE_NETWORK,
        RESOURCEDISPLAYTYPE_ROOT,
        RESOURCEDISPLAYTYPE_SHAREADMIN,
        RESOURCEDISPLAYTYPE_DIRECTORY,
        RESOURCEDISPLAYTYPE_TREE,
        RESOURCEDISPLAYTYPE_NDSCONTAINER
    };
    #endregion

    #region IDisposable Members
    public void Dispose()
    {
        Dispose(true);
    }
    #endregion

    #region overrides/virtuals
    public override string ToString()
    {
        return m_FullDriveLetter;
    }
    /// <param name="disposing"></param>
    protected virtual void Dispose(bool disposing)
    {
        if (!m_Disposed)
        {
            if (disposing)
            {
                DisconnectNetworkDrive();
            }
            m_Disposed = true;
        }
    }
    #endregion
}

}

NetWorkUNCPath.cs source code:

using System;
using System.Management;
namespace SeekCopySupportTool.Business
{

public class NetWorkUNCPath
{
    // get UNC path
    public static string GetUNCPath(string path)
    {
        if (path.StartsWith(@"\\"))
        {
            return path;
        }

        ManagementObject mo = new ManagementObject();
        mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", path));

        // DriveType 4 = Network Drive
        if (Convert.ToUInt32(mo["DriveType"]) == 4)
        {
            return Convert.ToString(mo["ProviderName"]);
        }
        // DriveType 3 = Local Drive
        else if (Convert.ToUInt32(mo["DriveType"]) == 3)
        {
            return "\\\\" + Environment.MachineName + "\\" + path.Substring(0,1) + "$";
        }
        else
        {
            return path;
        }
    }
}

}

Solution 8 - C#

This library might be helpful: Zeta Long Paths

Solution 9 - C#

In C# for me this is a workaround:

/*make long path short by setting it to like cd*/
string path = @"\\godDamnLong\Path\";
Directory.SetCurrentDirectory(path);

Solution 10 - C#

I have been surprise to discover that solution of Jason work perfectly well on my PC using Visual Studio 2019 and .Net Framework 4.7.2

Initially, I have written something similar to this code

Dim sBaseDir = "D:\Documents\+Informatique\Application\@Visual Basic.NET\GetTestAchatsPosts\Site\communication-multimedia\ordinateurs"
Dim sLastDir = "2638.pc-acer-ne-charge-plus-malgre-un-retour-en-garantie-reviens-charge-mais-ne-charge-toujours-pas-garantie-passee-plus-dintervention-gra"

System.IO.Directory.CreateDirectory(sBaseDir & "\" & sLastDir)

This code generates an error 429-PathTooLongException


I have then tested is creating only last directory

System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory(sLastDir)

This code return same error 429-PathTooLongException


I have then tested using a short base directory to see if problem is linked to length of last directory or full directory

System.IO.Directory.SetCurrentDirectory("D:\Documents")
System.IO.Directory.CreateDirectory(sLastDir)

This code work. But directory is created in bad location.


I have then tested is reducing name's length of last directory.

System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory(sLastDir.Substring(0, 100))

This code works, but last directory name is reducted !


I have then tested using proposal of Jason in 2 steps

System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory("\\?\" & sLastDir)

This code crashes indicating that directory syntax is not correct !


I have then also tested same syntax in adding "." string

System.IO.Directory.SetCurrentDirectory(sBaseDir)
System.IO.Directory.CreateDirectory("\\?\.\" & sLastDir)

This code crashes indicating that directory syntax is not correct !


I have then tested using a simple command prefixing full directory with "\\?"

System.IO.Directory.CreateDirectory("\\?\" & sBaseDir & "\" & sLastDir)

This code work perfectly and it is the best solution for me.

The only inconvenience is that directory must be a full directory.

Warning ! You cannot not mix "\" and "/" characters !

I have written following code

sQuestionDir = "\\?\" & sArticleDir & "/" & sQuestionDir
If Not System.IO.Directory.Exists(sQuestionDir) Then
    System.IO.Directory.CreateDirectory(sQuestionDir)
End If

and program crashes when executing CreateDirectory() function !

The correct code that is working is

sQuestionDir = "\\?\" & sArticleDir & "/" & sQuestionDir
If Not System.IO.Directory.Exists(sQuestionDir) Then
    System.IO.Directory.CreateDirectory(sQuestionDir)
End If

I hope that all these tests can help others to solve their problem.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontheKingView Question on Stackoverflow
Solution 1 - C#Mark HurdView Answer on Stackoverflow
Solution 2 - C#ElshanView Answer on Stackoverflow
Solution 3 - C#ShuggyCoUkView Answer on Stackoverflow
Solution 4 - C#BrandonView Answer on Stackoverflow
Solution 5 - C#Jason SView Answer on Stackoverflow
Solution 6 - C#A. HaraldView Answer on Stackoverflow
Solution 7 - C#alexView Answer on Stackoverflow
Solution 8 - C#JeffRSonView Answer on Stackoverflow
Solution 9 - C#CodingYourLifeView Answer on Stackoverflow
Solution 10 - C#schlebeView Answer on Stackoverflow