How to avoid System.IO.PathTooLongException?
Asked Answered
S

10

62

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?

Sentimentalism answered 9/2, 2009 at 21:38 Comment(5)
MAX_PATH is 260, the combination of the server/share probably make it 248 in your case.Fairweather
any final solution with full source code sample working about it ?Austriahungary
I have found out that maxpath is not really 260 but 259.Interlineate
@Interlineate MAX_PATH is really 260. There is a null character ending the string. See: msdn.microsoft.com/en-us/library/windows/desktop/…Playoff
You should check out my answer to a similar question. Windows 10 provides new functionality to manage long path behavior https://mcmap.net/q/166604/-best-way-to-resolve-file-path-too-long-exceptionPyroelectric
T
29

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

Tocci answered 11/8, 2016 at 7:11 Comment(1)
Your statement is not entirely true. The new .NET frameworks do not REMOVE the limitation...but they do offer a supported way to remove the limitation. It is not removed by default just by using the latest framework. You have to enable long path support in Windows (by either a registry change or Group Policy change). Then you must add appropriate code to your App.config file in your application for your application to support long paths. The link to Jeremy Kuhne's blog provides further details on these things. I really appreciate your answer though, as it pointed me to the solution.Oculist
S
12

Try This : Delimon.Win32.I​O Library (V4.0) This library 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

Slop answered 23/7, 2015 at 4:52 Comment(6)
This is a great library, thanks for sharing. You should put it on NuGet.Carrico
THis library is great, but why does Microsoft not provide native support for anything that the filesystem can actually handle?Celka
I need to use ZipFile.OpenRead() which the Delimon.dll does not support. Solution was copying the file with Delimon to a /temp directory and using ZipFile.OpenRead() afterwards. Just if anyone has a simmilar problem...Airworthy
This library is not viable for 64bit projects.Macruran
@AndreiMișcu This Libarary is written on .NET Framework 4.0 and can be used either on x86 & x64 systems.Slop
@Slop I think the complaint about not viable for 64bit projects stems from a bug users of your library have reported. It throws a OverflowException . See Q&A at gallery.technet.microsoft.com/…Damien
S
9

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)

Sauers answered 10/2, 2009 at 10:10 Comment(1)
The blog link has changed to learn.microsoft.com/en-us/archive/blogs/bclteam/…Riane
K
4

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

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

Krenek answered 9/2, 2009 at 21:42 Comment(1)
This article or the code itself does not address how you would create a new directory under a path that is already exceeded 260 chars.Sentimentalism
D
3

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":

Diversiform answered 7/5, 2012 at 20:25 Comment(0)
R
2

I used the "subst" command to work around the problem... http://www.techrepublic.com/article/mapping-drive-letters-to-local-folders-in-windows-xp/5975262

Reproachless answered 15/5, 2012 at 18:0 Comment(0)
O
2

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;
        }
    }
}

}
Oracular answered 12/1, 2018 at 16:39 Comment(0)
S
1

This library might be helpful: Zeta Long Paths

Sierrasiesser answered 25/6, 2013 at 11:10 Comment(0)
L
0

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 tested in creating only last directory

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

This code return same error 429-PathTooLongException


I tested then 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.

Leasehold answered 18/7, 2021 at 19:24 Comment(0)
P
-2

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);
Provencher answered 9/9, 2012 at 5:10 Comment(3)
Problem is, just about anything that expects to use files within the directory will also expect to get the real (full, absolute) name of those files, which is bound to be longer than MAX_PATH chars at some point.Homager
you mean if the filename is longer than the max_path? i don't think that's even possible. anything else you can go there step by step like in cmd "cd home", then "cd subfolderOfHome" etc. maybe you need to trick a little but it's all possible if you ask me...Provencher
A file name longer than MAX_PATH chars is possible. Win32 provides ways to create files with a path over 32k chars long. (MAX_PATH is something like 260 as of Windows 7.) The default, though, is to restrict. As for whether .net lets you...that's the big question, and it doesn't look like this answers it.Homager

© 2022 - 2024 — McMap. All rights reserved.