Muhammad Raja
Muhammad Raja

Reputation: 2010

Best way to resolve file path too long exception

I created a app that downloads all document libraries in a SP Site , but at one point it giving me this error (I tried looking at google but couldn;t find anything, now if anyone knows any trick to solve this problem please respond otherwise thanks for looking at it)

System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters. at System.IO.Path.NormalizePathFast(String path, Boolean fullCheck) at System.IO.Path.GetFullPathInternal(String path) at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options) at System.IO.File.Create(String path)

it reaches the limit for string, Code is given below,

#region Downloading Schemes

    private void btnDownload_Click(object sender, EventArgs e)
    {
        TreeNode currentNode = tvWebs.SelectedNode;
        SPObjectData objectData = (SPObjectData)currentNode.Tag;
        try
        {
            CreateLoggingFile();
            using (SPWeb TopLevelWeb = objectData.Web)
            {
                if(TopLevelWeb != null)
                    dwnEachWeb(TopLevelWeb, TopLevelWeb.Title, tbDirectory.Text);
            }
        }
        catch (Exception ex)
        {
            Trace.WriteLine(string.Format("Exception caught when tried to pass TopLevelWeb:{1}, Title = {2}, object data to (dwnEachWeb_method), Exception: {0}", ex.ToString(), objectData.Web, objectData.Title));
        }
        finally
        {
            CloseLoggingFile();
        }
    }

    private void dwnEachWeb(SPWeb TopLevelWeb, string FolderName, string CurrentDirectory)
    {
        if (TopLevelWeb != null)
        {
            if (TopLevelWeb.Webs != null)
            {
                CurrentDirectory = CurrentDirectory + "\\" + TopLevelWeb.Title;
                CreateFolder(CurrentDirectory);
                foreach (SPWeb ChildWeb in TopLevelWeb.Webs)
                {

                    dwnEachWeb(ChildWeb, ChildWeb.Title, CurrentDirectory);
                    ChildWeb.Dispose();
                }
                dwnEachList(TopLevelWeb, CurrentDirectory);
                //dwnEachList(TopLevelWeb, FolderName, CurrentDirectory);
            }
        }
    }

    private void dwnEachList(SPWeb oWeb, string CurrentDirectory)
    {
        foreach (SPList oList in oWeb.Lists)
        {
            if (oList is SPDocumentLibrary && !oList.Hidden)
            {
                dwnEachFile(oList.RootFolder, CurrentDirectory);
            }
        }
    }

    private void dwnEachFile(SPFolder oFolder, string CurrentDirectory)
    {
        if (oFolder.Files.Count != 0)
        {
            CurrentDirectory = CurrentDirectory + "\\" + oFolder.Name;
            CreateFolder(CurrentDirectory);
            foreach (SPFile ofile in oFolder.Files)
            {
                if (CreateDirectoryStructure(CurrentDirectory, ofile.Url))
                {
                    var filepath = System.IO.Path.Combine(CurrentDirectory, ofile.Url);
                    byte[] binFile = ofile.OpenBinary();
                    System.IO.FileStream fstream = System.IO.File.Create(filepath);
                    fstream.Write(binFile, 0, binFile.Length);
                    fstream.Close();
                }
            }
        }
    }

    //creating directory where files will be download        
    private bool CreateDirectoryStructure(string baseFolder, string filepath)
    {
        if (!Directory.Exists(baseFolder)) return false;

        var paths = filepath.Split('/');

        for (var i = 0; i < paths.Length - 1; i++)
        {
            baseFolder = System.IO.Path.Combine(baseFolder, paths[i]);
            Directory.CreateDirectory(baseFolder);
        }
        return true;
    }

    //creating folders
    private bool CreateFolder(string CurrentDirectory)
    {
        if (!Directory.Exists(CurrentDirectory))
        {
            Directory.CreateDirectory(CurrentDirectory);
        }
        return true;
    }

    //shorting string

    #endregion

Upvotes: 139

Views: 372394

Answers (14)

arnobpl
arnobpl

Reputation: 1236

Here are the tasks (verified that it works for long file paths) in addition to the previous answer:

  1. Create the utility class to call Shell32 API's from C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;

namespace SymlinkCreator.ui.utility
{
    internal static class LongPathAware
    {
        #region constants

        private const string ShellIdListArrayName = "Shell IDList Array";
        private const uint SIGDN_FILESYSPATH = 0x80058000;

        #endregion


        #region methods

        public static IEnumerable<string> GetPathsFromShellIdListArray(IDataObject data)
        {
            if (!data.GetDataPresent(ShellIdListArrayName)) yield break;

            using (MemoryStream ms = (MemoryStream)data.GetData(ShellIdListArrayName))
            {
                byte[] bytes = ms.ToArray();
                IntPtr p = Marshal.AllocHGlobal(bytes.Length);

                try
                {
                    Marshal.Copy(bytes, 0, p, bytes.Length);
                    uint cidl = (uint)Marshal.ReadInt32(p);
                    int offset = sizeof(uint);
                    IntPtr parentpidl = (IntPtr)((long)p + Marshal.ReadInt32(p, offset));

                    for (int i = 1; i <= cidl; ++i)
                    {
                        offset += sizeof(uint);
                        IntPtr relpidl = (IntPtr)((long)p + Marshal.ReadInt32(p, offset));
                        IntPtr abspidl = ILCombine(parentpidl, relpidl);

                        if (abspidl == IntPtr.Zero) continue;

                        if (SHGetNameFromIDList(abspidl, SIGDN_FILESYSPATH, out IntPtr pszName) == 0)
                        {
                            yield return Marshal.PtrToStringUni(pszName);
                            Marshal.FreeCoTaskMem(pszName);
                        }

                        ILFree(abspidl);
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(p);
                }
            }
        }

        #endregion


        #region external methods

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        public static extern int SHGetNameFromIDList(IntPtr pidl, uint sigdnName, out IntPtr ppszName);

        [DllImport("shell32.dll")]
        public static extern IntPtr ILCombine(IntPtr pidl1, IntPtr pidl2);

        [DllImport("shell32.dll")]
        public static extern void ILFree(IntPtr pidl);

        #endregion
    }
}

  1. Use the above static method in the event handler like this (notice that the utility method is used as a fallback method):
        private string[] GetDroppedFileOrFolderList(DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                try
                {
                    return (string[])e.Data.GetData(DataFormats.FileDrop);
                }
                catch (COMException) // Handle long-path scenarios
                {
                    return LongPathAware.GetPathsFromShellIdListArray(e.Data).ToArray();
                }
            }

            return null;
        }

        private void SourceFileOrFolderListView_OnDrop(object sender, DragEventArgs e)
        {
            string[] droppedFileOrFolderList = GetDroppedFileOrFolderList(e);
            if (droppedFileOrFolderList != null)
            {
                // Do something here
            }
        }

This should return the long path list as expected (it works well even for more than one dropped file).

Credit: The answer was inspired from another StackOverflow question's answer.

Upvotes: 0

Moaad
Moaad

Reputation: 137

The folowing worked with me on Windows 10 using visual studio 2022 :

run the following powershell script :

New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" ` -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force

restart your pc and run visual studio again.

if you dont want to change the registery move the project to a shorter path destination.

Upvotes: 0

goonerify
goonerify

Reputation: 1756

The solution that worked for me was to edit the registry key to enable long path behaviour, setting the value to 1. This is a new opt-in feature for Windows 10

HKLM\SYSTEM\CurrentControlSet\Control\FileSystem LongPathsEnabled (Type: REG_DWORD)

I got this solution from a named section of the article that @james-hill posted.

Naming Files, Paths, and Namespaces: Maximum Path Length Limitation

In editions of Windows before Windows 10 version 1607, the maximum length for a path is MAX_PATH, which is defined as 260 characters. In later versions of Windows, changing a registry key or using the Group Policy tool is required to remove the limit. See Maximum Path Length Limitation for full details.


See also Maximum Path Length Limitation mentioned by @dontbyteme.

To enable the new long path behavior, both of the following conditions must be met:

  • The registry key Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled (Type: REG_DWORD) must exist and be set to 1. The key's value will be cached by the system (per process) after the first call to an affected Win32 file or directory function (see below for the list of functions). The registry key will not be reloaded during the lifetime of the process. In order for all apps on the system to recognize the value of the key, a reboot might be required because some processes may have started before the key was set.
  • The application manifest must also include the longPathAware element.
<application xmlns="urn:schemas-microsoft-com:asm.v3">
   <windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
       <ws2:longPathAware>true</ws2:longPathAware>
   </windowsSettings>
</application>

Upvotes: 60

Elshan
Elshan

Reputation: 7693

Apps that target the .NET Framework 4.6.2 and later versions Long paths are supported by default. The runtime throws a PathTooLongException under the following conditions:

  • The operating system returns COR_E_PATHTOOLONG or its equivalent.
  • The length of the path exceeds Int16.MaxValue (32,767) characters.

https://learn.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception?view=net-6.0

Upvotes: 0

user3820036
user3820036

Reputation: 13

this may be also possibly solution.It some times also occurs when you keep your Development project into too deep, means may be possible project directory may have too many directories so please don't make too many directories keep it in a simple folder inside the drives. For Example- I was also getting this error when my project was kept like this-

D:\Sharad\LatestWorkings\GenericSurveyApplication020120\GenericSurveyApplication\GenericSurveyApplication

then I simply Pasted my project inside

D:\Sharad\LatestWorkings\GenericSurveyApplication

And Problem was solved.

Upvotes: 0

Riyaz Hameed
Riyaz Hameed

Reputation: 1103

From my experience, won't recommend my below answer for any public facing Web applications.

If you need it for your inhouse tools or for Testing, I would recommend to share it on your own machine.

-Right click on the root path you need to access
-Choose Properties
-Click on Share button and add your chosen users who can access it

This will then create a shared directory like \\{PCName}\{YourSharedRootDirectory} This could be definitely much less than your full path I hope, for me I could reduce to 30 characters from about 290 characters. :)

Upvotes: 0

Jatin Nath Prusty
Jatin Nath Prusty

Reputation: 572

The best answer I can find, is in one of the comments here. Adding it to the answer so that someone won't miss the comment and should definitely try this out. It fixed the issue for me.

We need to map the solution folder to a drive using the "subst" command in command prompt- e.g., subst z:

And then open the solution from this drive (z in this case). This would shorten the path as much as possible and could solve the lengthy filename issue.

Upvotes: 0

Markus
Markus

Reputation: 839

Not mention so far and an update, there is a very well establish library for handling paths that are too long. AlphaFS is a .NET library providing more complete Win32 file system functionality to the .NET platform than the standard System.IO classes. The most notable deficiency of the standard .NET System.IO is the lack of support of advanced NTFS features, most notably extended length path support (eg. file/directory paths longer than 260 characters).

Upvotes: 2

Lachezar Lalov
Lachezar Lalov

Reputation: 517

What worked for me is moving my project as it was on the desktop (C:\Users\lachezar.l\Desktop\MyFolder) to (C:\0\MyFolder) which as you can see uses shorter path and reducing it solved the problem.

Upvotes: 6

N-ate
N-ate

Reputation: 6943

If you are having an issue with your bin files due to a long path, In Visual Studio 2015 you can go to the offending project's property page and change the relative Output Directory to a shorter one.

E.g. bin\debug\ becomes C:\_bins\MyProject\

Upvotes: 2

Markus Weber
Markus Weber

Reputation: 1107

You can create a symbolic link with a shorter directory. First open command line for example by Shift + RightClick in your desired folder with a shorter path (you may have to run it as administrator).

Then type with relative or absolute paths:

mklink ShortPath\To\YourLinkedSolution C:\Path\To\Your\Solution /D

And then start the Solution from the shorter path. The advantage here is: You don't have to move anything.

Upvotes: 9

James Hill
James Hill

Reputation: 61862

As the cause of the error is obvious, here's some information that should help you solve the problem:

See this MS article about Naming Files, Paths, and Namespaces

Here's a quote from the link:

Maximum Path Length Limitation In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters. A local path is structured in the following order: drive letter, colon, backslash, name components separated by backslashes, and a terminating null character. For example, the maximum path on drive D is "D:\some 256-character path string<NUL>" where "<NUL>" represents the invisible terminating null character for the current system codepage. (The characters < > are used here for visual clarity and cannot be part of a valid path string.)

And a few workarounds (taken from the comments):

There are ways to solve the various problems. The basic idea of the solutions listed below is always the same: Reduce the path-length in order to have path-length + name-length < MAX_PATH. You may:

  • Share a subfolder
  • Use the commandline to assign a drive letter by means of SUBST
  • Use AddConnection under VB to assign a drive letter to a path

Upvotes: 70

Tim Lewis
Tim Lewis

Reputation: 3445

There's a library called Zeta Long Paths that provides a .NET API to work with long paths.

Here's a good article that covers this issue for both .NET and PowerShell: ".NET, PowerShell Path too Long Exception and a .NET PowerShell Robocopy Clone"

Upvotes: 27

Marcel Piquet
Marcel Piquet

Reputation: 168

On Windows 8.1, using. NET 3.5, I had a similar problem.
Although the name of my file was only 239 characters length when I went to instantiate a FileInfo object with just the file name (without path) occurred an exception of type System. IO.PathTooLongException

2014-01-22 11:10:35 DEBUG LogicalDOCOutlookAddIn.LogicalDOCAddIn - fileName.Length: 239 
2014-01-22 11:10:35 ERROR LogicalDOCOutlookAddIn.LogicalDOCAddIn - Exception in ImportEmail System.IO.PathTooLongException: Percorso e/o nome di file specificato troppo lungo. Il nome di file completo deve contenere meno di 260 caratteri, mentre il nome di directory deve contenere meno di 248 caratteri.
   in System.IO.Path.NormalizePathFast(String path, Boolean fullCheck)
   in System.IO.FileInfo..ctor(String fileName)
   in LogicalDOCOutlookAddIn.LogicalDOCAddIn.GetTempFilePath(String fileName) in C:\Users\alle\Documents\Visual Studio 2010\Projects\MyAddin1Outlook20072010\MyAddin1Outlook20072010\LogicalDOCAddIn.cs:riga 692
   in LogicalDOCOutlookAddIn.LogicalDOCAddIn.ImportEmail(_MailItem mailItem, OutlookConfigXML configXML, Int64 targetFolderID, String SID) in C:\Users\alle\Documents\Visual Studio 2010\Projects\MyAddin1Outlook20072010\MyAddin1Outlook20072010\LogicalDOCAddIn.cs:riga 857
   in LogicalDOCOutlookAddIn.LogicalDOCAddIn.ImportEmails(Explorers explorers, OutlookConfigXML configXML, Int64 targetFolderID, Boolean suppressResultMB) in C:\Users\alle\Documents\Visual Studio 2010\Projects\MyAddin1Outlook20072010\MyAddin1Outlook20072010\LogicalDOCAddIn.cs:riga 99

I resolved the problem trimming the file name to 204 characters (extension included).

Upvotes: 2

Related Questions