System.IO.DirectoryNotFoundException on File.Delete for a valid path

834 Views Asked by At

I wrote code to delete a folder with all its structure recursively.

I got System.IO.DirectoryNotFoundException on File.Delete for a valid filename that I got from Directory.GetFiles(path). I added File.Exists before and it tells me that the file does not exists either of I can see it in Explorer.

I know I can do it with SHFileOperation which should work fine. But I would like to use native C# (no direct Windows API).

I red: .NET 4.6.2 and long paths on Windows 10 and set enable "Enable Win 32 long paths" in local group policy editor.

I'm using .NET Framework 4.7.2.

Does anyone can tell me what's wrong in my code? Or what I can do to make it works?

The path in error is:

'E:\CobianBackupOld\cn1629\AppData\Local\Packages\Microsoft.Windows.Cortana_cw5n1h2txyewy\LocalState\AppIconCache\100\{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}_Microsoft Visual Studio 9_0_Application_PreEmptive Solutions_Dotfuscator Community Edition_dotfuscator_exe'

I'm the owner of that file (I do have privilege to delete it).

UPDATE

I partly fixed my error but would like to know how to fix it properly (Recent frameworks are suppose to support long path???). My actual fix is to add that code at start of my recursive function

if (path.Length < 2 || !path.StartsWith(@"\\"))
{
  path = @"\\?\" + path;
}

UPDATE 2 Although not directly related... Just want to bring an important point, C# File.Delete or Directory.Delete does NOT delete readonly file or folder, you should also change their permissions to normal before deleting them, otherwise you get an access denied error.

FYI, my code is:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HQ.Util.General.IO
{
    public class DirectoryRemoverRecursive
    {
        private readonly Action<string, string> _pathStatus;
        private readonly Func<string, bool> _callbackCanRemoveFile;
        private readonly Func<string, bool> _callbackCanRemoveFolder;
        private Func<bool> _shouldCancel;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pathStatus">Arguments are [path] and [null on success or exception message]</param>
        /// <param name="callbackCanRemoveFile">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
        /// <param name="callbackCanRemoveFolder">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
        /// <param name="shouldCancel">If null will never cancel. Cancel when func return true</param>
        public DirectoryRemoverRecursive(
            Action<string, string> pathStatus = null, 
            Func<string, bool> callbackCanRemoveFile = null, 
            Func<string, bool> callbackCanRemoveFolder = null, 
            Func<bool> shouldCancel = null)
        {
            _pathStatus = pathStatus;
            _callbackCanRemoveFile = callbackCanRemoveFile;
            _callbackCanRemoveFolder = callbackCanRemoveFolder;
            _shouldCancel = shouldCancel;
        }

        // ******************************************************************
        /// <summary>
        /// return true if canceled
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool Remove(string path)
        {
            string result = null;

            if (Directory.Exists(path))
            {
                foreach (var subDir in Directory.GetDirectories(path))
                {
                    if (_shouldCancel != null)
                    {
                        if (_shouldCancel())
                        {
                            return true;
                        }
                    }

                    if (Remove(subDir))
                    {
                        return true;
                    }
                }

                foreach (var filename in Directory.GetFiles(path))
                {
                    if (_shouldCancel != null)
                    {
                        if (_shouldCancel())
                        {
                            return true;
                        }
                    }

                    if (Remove(filename))
                    {
                        return true;
                    }
                }

                try
                {
                    if (_callbackCanRemoveFolder != null)
                    {
                        if (!_callbackCanRemoveFolder(path))
                        {
                            return false;
                        }
                    }

                    Directory.Delete(path);
                    result = null;
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
            }
            else
            {
                try
                {
                    if (File.Exists(path))
                    {
                        if (_callbackCanRemoveFile != null)
                        {
                            if (!_callbackCanRemoveFile(path))
                            {
                                return false;
                            }
                        }

                        File.Delete(path);
                        result = null;
                    }
                    else
                    {
                        Debug.Print($"File does not exists {path}");
                    }
                }
                catch (Exception ex)
                {
                    result = ex.Message;
                }
            }

            _pathStatus?.Invoke(path, result);

            return false;
        }

        // ******************************************************************

    }

}
1

There are 1 best solutions below

0
On BEST ANSWER

Although no as perfect as I would have wished, I fixed my bugs by using prefix "\?\" and by ensuring that attributes are set to normal prior to delete.

I still don't undertsand why I still have to do that??? Why it is still not fixed in recent .net library?

Final code:

using System;
using System.Diagnostics;
using System.IO;

namespace HQ.Util.General.IO
{
    public class DirectoryRemoverRecursive
    {
        private readonly Action<string, string, int> _pathStatus;
        private readonly Func<string, bool> _callbackCanRemoveFile;
        private readonly Func<string, bool> _callbackCanRemoveFolder;
        private Func<bool> _shouldCancel;

        public int Count { get; private set; } = 0;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="pathStatus">Arguments are [path] and [null on success or exception message]</param>
        /// <param name="callbackCanRemoveFile">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
        /// <param name="callbackCanRemoveFolder">Argument is path and should return true to delete. If this function is null, all path will be deleted.</param>
        /// <param name="shouldCancel">If null will never cancel. Cancel when func return true</param>
        public DirectoryRemoverRecursive(
            Action<string, string, int> pathStatus = null, 
            Func<string, bool> callbackCanRemoveFile = null, 
            Func<string, bool> callbackCanRemoveFolder = null, 
            Func<bool> shouldCancel = null)
        {
            _pathStatus = pathStatus;
            _callbackCanRemoveFile = callbackCanRemoveFile;
            _callbackCanRemoveFolder = callbackCanRemoveFolder;
            _shouldCancel = shouldCancel;
        }

        // ******************************************************************
        /// <summary>
        /// return true if canceled
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool Remove(string path)
        {
            string result = null;

            if (path.Length < 2 || !path.StartsWith(@"\\"))
            {
                path = @"\\?\" + path;
            }

            if (Directory.Exists(path))
            {
                foreach (var subDir in Directory.GetDirectories(path))
                {
                    if (_shouldCancel != null)
                    {
                        if (_shouldCancel())
                        {
                            return true;
                        }
                    }

                    if (Remove(subDir))
                    {
                        return true;
                    }
                }

                foreach (var filename in Directory.GetFiles(path))
                {
                    if (_shouldCancel != null)
                    {
                        if (_shouldCancel())
                        {
                            return true;
                        }
                    }

                    if (Remove(filename))
                    {
                        return true;
                    }
                }

                try
                {
                    if (_callbackCanRemoveFolder != null)
                    {
                        if (!_callbackCanRemoveFolder(path))
                        {
                            return false;
                        }
                    }

                    Directory.Delete(path);
                    Count++;
                    result = null;
                }
                catch (Exception ex)
                {
                    try
                    {
                        File.SetAttributes(path, FileAttributes.Normal);
                        Directory.Delete(path);
                        Count++;
                        result = null;
                    }
                    catch (Exception)
                    {
                        result = "Try to delete directory exception: " + ex.ToString();
                    }
                }
            }
            else
            {
                try
                {
                    if (File.Exists(path))
                    {
                        if (_callbackCanRemoveFile != null)
                        {
                            if (!_callbackCanRemoveFile(path))
                            {
                                return false;
                            }
                        }

                        File.Delete(path);
                        Count++;
                        result = null;
                    }
                    else
                    {
                        Debug.Print($"File does not exists {path}");
                    }
                }
                catch (Exception ex)
                {
                    try
                    {
                        File.SetAttributes(path, FileAttributes.Normal);
                        File.Delete(path);
                        Count++;
                        result = null;
                    }
                    catch (Exception)
                    {
                        result = "Try to delete file exception: " + ex.ToString();
                    }
                }
            }

            _pathStatus?.Invoke(path, result, Count);

            return false;
        }

        // ******************************************************************

    }

}