C# deleting a folder that has long paths
Asked Answered
T

10

17

I'm trying to delete a folder and the delete is failing due to the folder containing long paths. I presume I need to use something else instead of dir.Delete(true), Anyone crossed this bridge before?

Many thanks

 try
{
 var dir = new DirectoryInfo(@FolderPath);
 dir.Attributes = dir.Attributes & ~FileAttributes.ReadOnly;
 dir.Delete(true);
}
catch (IOException ex)
{
 MessageBox.Show(ex.Message);
}

This is the path in question: \server\share\dave\Private\Careers\Careers Ed\Fun Careers Education\Chris's not used 2006 to07\old 4.Careers Area Activity Week 1 30.10.06 or 6.11.06 or 13.11.06 Introduction to job levels and careers resources\Occupational Areas & Job levels Tutor Help Sheet[1].doc

Tip answered 8/2, 2010 at 16:19 Comment(2)
How long are the paths: codinghorror.com/blog/archives/000729.htmlGillman
Did you try the win32 function call with unicode path?Desirous
G
13

In the Windows API, 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.) [MSDN]

The Unicode versions of several functions permit a maximum path length of approximately 32,000 characters composed of components up to 255 characters in length. To specify that kind of path, use the "\\?\" prefix. The maximum path of 32,000 characters is approximate, because the "\\?\" prefix can be expanded to a longer string, and the expansion applies to the total length.

For example, "\\?\D:\<path>". To specify such a UNC path, use the "\\?\UNC\" prefix. For example, "\\?\UNC\<server>\<share>". These prefixes are not used as part of the path itself. They indicate that the path should be passed to the system with minimal modification, which means that you cannot use forward slashes to represent path separators, or a period to represent the current directory. Also, you cannot use the "\\?\" prefix with a relative path. Relative paths are limited to MAX_PATH characters.

The shell and the file system may have different requirements. It is possible to create a path with the API that the shell UI cannot handle.

C# syntax:

[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool DeleteFile(string path);

For more information on the class, see System Namespace - MSDN

Excerpts from:

Filesystem Paths: How Long is Too Long? - Coding Horror

DeleteFile function (Windows) - MSDN

Gillman answered 8/2, 2010 at 16:24 Comment(2)
Hi, I've just tried this and I get the same error "Could not find part of the path 'long path and file name'". Do you know of anything else I can try? ThanksTip
This works I tried it, works with a a path 300 characters long for me.Gillman
A
4

The 260-character limitation (I assume that's the one you're running into) is an issue in Windows, not in .NET, unfortunately, so working around it can be difficult.

You might be able to work around it by changing your working directory such that the relative path for the delete is less than 260 characters; I don't know if that will work or not.

i.e.:

var curDir = Directory.GetCurrentDirectory();
Environment.CurrentDirectory = @"C:\Part\Of\The\Really\Long\Path";
Directory.Delete(@"Relative\Path\To\Directory");
Environment.CurrentDirectory = curDir;
Alexandraalexandre answered 8/2, 2010 at 16:38 Comment(1)
you are missing a @ in Directory.Delete("Relative\Path\To\Directory");Hortative
D
4

Check the Win32 API: http://msdn.microsoft.com/en-us/library/aa363915%28VS.85%29.aspx

There it states: "In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\?\" to the path."

Add the pinvoke:

using System;  
using System.Runtime.InteropServices;  
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]  
[return: MarshalAs(UnmanagedType.Bool)]  
internal static extern bool DeleteFile(string lpFileName);

Use it:

public static void DeleteLong(string fileName) {

    string LongName = @"\\?\" + fileName;
    DeleteFile(formattedName);
}
Desirous answered 8/2, 2010 at 16:48 Comment(2)
Starting in Windows 10, version 1607, for the unicode version of this function (DeleteFileW), you can opt-in to remove the MAX_PATH character limitation without prepending "\\?\". See the "Maximum Path Limitation" section of Naming Files, Paths, and Namespaces for details.Delectation
The DeleteFile() API works on files not directories. RemoveDirectory() is the correct API.Emmieemmit
H
4

best I have so far is this

public static class IOHelper
{
    public static void DeleteDirectory(DirectoryInfo directoryInfo)
    {
        var emptyTempDirectory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "IOHelperEmptyDirectory"));
        emptyTempDirectory.Create();
        var arguments = string.Format("\"{0}\" \"{1}\" /MIR", emptyTempDirectory.FullName, directoryInfo.FullName);
        using (var process = Process.Start(new ProcessStartInfo("robocopy")
                                            {
                                                Arguments = arguments,
                                                CreateNoWindow = true,
                                                UseShellExecute = false,
                                            }))
        {
            process.WaitForExit();
        }
        directoryInfo.Delete();
    }
}
Hortative answered 21/10, 2010 at 6:5 Comment(2)
It would be good if you explain what robocopy is and why do you use /MIRPolysepalous
Also, this doesn't work for all situations -- as it does not in mine. Neither the 'MIR' or 'PURGE' options from Robocopy worked. Or the rename to < 260, or the iterative drive mounting. I made it 2000 levels deep before I called it quits.Serialize
H
3

I don't know if this question is still open, but I solved the problem. Code was developed on a win7 machine, with VS2008. These are the steps I followed to solve the problem

  1. Create an empty VS C# project
  2. Add a reference to this COM Object: microsoft scripting runtime
  3. Add a using Scripting; to your using list
  4. Somewhere in your code, create a function resembling this:

    private static void DeletePathWithLongFileNames(string path)
    {
        var tmpPath = @"\\?\"+ path
        FileSystemObject fso = new FileSystemObjectClass() as FileSystemObject;
        fso.DeleteFolder(tmpPath, true); 
    }
    

Path parameter is the directory you want to delete. The function prepends the unicode path signature, creates a FileSystemObject instances and deletes the unicode path and all of its contents.

  • Compile the program, launch the resulting .exe files with administrative privileges (run as Administrator on win7), seek out the directory you want to delete and apply this function on it. Then, watch long file names go.

Needless to say, it is powerful... and dangerous. With Great Power Comes Great Responsibility :-)

Harleigh answered 21/12, 2011 at 10:9 Comment(0)
R
2

This is what I use for deleting home directories where long paths often occur:

public static void DirectoryDeleteLong(string directoryPath)
{
    var emptyDirectory = new DirectoryInfo(Path.GetTempPath() + "\\TempEmptyDirectory-" + Guid.NewGuid());
    try
    {
        emptyDirectory.Create();
        using (var process = new Process())
        {
            process.StartInfo.FileName = "robocopy.exe";
            process.StartInfo.Arguments = "\"" + emptyDirectory.FullName + "\" \"" + directoryPath + "\" /mir /r:1 /w:1 /np /xj /sl";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.Start();
            process.WaitForExit();
        }
        emptyDirectory.Delete();
        new DirectoryInfo(directoryPath).Attributes = FileAttributes.Normal;
        Directory.Delete(directoryPath);
    }
    catch(IOException) { }
}

It is similar to the solution that Simon posted, but also:

  • Reduces robocopy's high default retry limit.
  • Resets attributes as directory.delete will fail on anything marked as read only.
  • Creates a unique empty directory name so it works with multiple threads.
Rummy answered 19/10, 2011 at 13:53 Comment(0)
M
0

You could try using p/invoke to grab the "short" path name using the GetShortPathName function from kernel32.dll:

http://www.pinvoke.net/default.aspx/kernel32.GetShortPathName

Mauve answered 8/2, 2010 at 16:41 Comment(0)
W
0

The following link shows the .NET internal implementation for long path support within System.IO, it's not the easiest to read as generated via Reflector but contains lots of examples of working with the Win32 APIs mentioned previously.

http://reflector.webtropy.com/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/clr/src/BCL/System/IO/LongPath@cs/1305376/LongPath@cs

It would be nice if this functionality was available through System.IO as the support is obviously there!

Worn answered 25/2, 2011 at 10:43 Comment(0)
P
0

I have created a Managed .Net library to work files and folders.

https://github.com/DotNetIO

var fs = LocalFileSystem.Instance : FileSystem

^^^^ place in IoC

fs.GetDirectory(@"C:\\a very very long path ...\with\subdirs\and files.txt").Delete();

Cheers

Plains answered 8/10, 2012 at 14:35 Comment(0)
D
0

You can use the C# Apis with Long File Name notation "\\?\<driveLetter>:\..."
This works both with System.IO.File.Delete and System.IO.Directory.Delete(...)

An issue you might experience is that Directory.Delete(path, true) (recursive delete) does not work with the exception that part of the path cannot be found. In that case ensure that the path does not have a trailing slash and all slashes in the path are \ backslashes

Dexamyl answered 21/9, 2023 at 8:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.