我知道这是旧线程,但除了 Jan Jongboom 的回答之外,我还提出了类似的解决方案,它非常高效且更通用。我的解决方案旨在快速删除 DFS 中的目录结构,并支持长文件名(>255 个字符)。第一个区别在于 DLL 导入声明。
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern IntPtr FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool FindNextFile(IntPtr hDindFile, ref WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MashalAs(UnmanagedType.Bool]
static extern bool DeleteFile(string lpFileName)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MashalAs(UnmanagedType.Bool]
static extern bool DeleteDirectory(string lpPathName)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool FindClose(IntPtr hFindFile);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLAstError = true)]
static extern uint GetFileAttributes(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLAstError = true)]
static extern bool SetFileAttributes(string lpFileName, uint dwFileAttributes);
WIN32_FIND_DATA 结构也略有不同:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode), Serializable, BestFitMapping(false)]
internal struct WIN32_FIND_DATA
{
internal FileAttributes dwFileAttributes;
internal FILETIME ftCreationTime;
internal FILETIME ftLastAccessTime;
internal FILETIME ftLastWriteTime;
internal int nFileSizeHigh;
internal int nFileSizeLow;
internal int dwReserved0;
internal int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
internal string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
internal string cAlternative;
}
为了使用长路径,路径需要准备如下:
public void RemoveDirectory(string directoryPath)
{
var path = @"\\?\UNC\" + directoryPath.Trim(@" \/".ToCharArray());
SearchAndDelete(path);
}
这是主要方法:
private void SearchAndDelete(string path)
{
var fd = new WIN32_FIND_DATA();
var found = false;
var handle = IntPtr.Zero;
var invalidHandle = new IntPtr(-1);
var fileAttributeDir = 0x00000010;
var filesToRemove = new List<string>();
try
{
handle = FindFirsFile(path + @"\*", ref fd);
if (handle == invalidHandle) return;
do
{
var current = fd.cFileName;
if (((int)fd.dwFileAttributes & fileAttributeDir) != 0)
{
if (current != "." && current != "..")
{
var newPath = Path.Combine(path, current);
SearchAndDelete(newPath);
}
}
else
{
filesToRemove.Add(Path.Combine(path, current));
}
found = FindNextFile(handle, ref fd);
} while (found);
}
finally
{
FindClose(handle);
}
try
{
object lockSource = new Object();
var exceptions = new List<Exception>();
Parallel.ForEach(filesToRemove, file, =>
{
var attrs = GetFileAttributes(file);
attrs &= ~(uint)0x00000002; // hidden
attrs &= ~(uint)0x00000001; // read-only
SetFileAttributes(file, attrs);
if (!DeleteFile(file))
{
var msg = string.Format("Cannot remove file {0}.{1}{2}", file.Replace(@"\\?\UNC", @"\"), Environment.NewLine, new Win32Exception(Marshal.GetLastWin32Error()).Message);
lock(lockSource)
{
exceptions.Add(new Exceptions(msg));
}
}
});
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}
var dirAttr = GetFileAttributes(path);
dirAttr &= ~(uint)0x00000002; // hidden
dirAttr &= ~(uint)0x00000001; // read-only
SetfileAttributtes(path, dirAttr);
if (!RemoveDirectory(path))
{
throw new Exception(new Win32Exception(Marshal.GetLAstWin32Error()));
}
}
当然,我们可以更进一步,将目录存储在该方法之外的单独列表中,稍后在另一种可能如下所示的方法中删除它们:
private void DeleteDirectoryTree(List<string> directories)
{
// group directories by depth level and order it by level descending
var data = directories.GroupBy(d => d.Split('\\'),
d => d,
(key, dirs) => new
{
Level = key,
Directories = dirs.ToList()
}).OrderByDescending(l => l.Level);
var exceptions = new List<Exception>();
var lockSource = new Object();
foreach (var level in data)
{
Parallel.ForEach(level.Directories, dir =>
{
var attrs = GetFileAttributes(dir);
attrs &= ~(uint)0x00000002; // hidden
attrs &= ~(uint)0x00000001; // read-only
SetFileAttributes(dir, attrs);
if (!RemoveDirectory(dir))
{
var msg = string.Format("Cannot remove directory {0}.{1}{2}", dir.Replace(@"\\?\UNC\", string.Empty), Environment.NewLine, new Win32Exception(Marshal.GetLastWin32Error()).Message);
lock (lockSource)
{
exceptions.Add(new Exception(msg));
}
}
});
}
if (exceptions.Any())
{
throw new AggregateException(exceptions);
}
}