所以我正在编写一个程序来扫描计算机上的重复文件,因为我看到的程序非常慢,和/或内存占用,但是PathTooLongException
当我尝试整个驱动器时我遇到了。在阅读了 C# 代码中的 PathTooLongException之后,我对以下两个问题感到好奇。
如果每次更改级别时都切换当前目录会影响我的表现吗?
有没有更好的方法来获取所有文件的目录结构(也许通过调用 tree.exe 之类的东西然后解析它)?
所以我正在编写一个程序来扫描计算机上的重复文件,因为我看到的程序非常慢,和/或内存占用,但是PathTooLongException
当我尝试整个驱动器时我遇到了。在阅读了 C# 代码中的 PathTooLongException之后,我对以下两个问题感到好奇。
如果每次更改级别时都切换当前目录会影响我的表现吗?
有没有更好的方法来获取所有文件的目录结构(也许通过调用 tree.exe 之类的东西然后解析它)?
或者自己做,
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr FindFirstFile(string lpFileName, out
WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern bool FindNextFile(IntPtr hFindFile, out
WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FindClose(IntPtr hFindFile);
// Assume dirName passed in is already prefixed with \\?\
public static IEnumerable<string> EnumerateEntries(string directory)
{
WIN32_FIND_DATA findData;
IntPtr findHandle = FindFirstFile(dirName + @"\*", out findData);
try
{
if (findHandle != INVALID_HANDLE_VALUE)
{
bool found;
do
{
string currentFileName = findData.cFileName;
// if this is a directory, find its contents
if (((int)findData.dwFileAttributes &
FILE_ATTRIBUTE_DIRECTORY) != 0)
{
if (currentFileName != "." && currentFileName != "..")
{
foreach(var child in FindFilesAndDirs(
Path.Combine(dirName, currentFileName))
{
yield return child;
}
}
}
yield return Path.Combine(dirName, currentFileName);
// find next
found = FindNextFile(findHandle, out findData);
}
while (found);
}
}
finally
{
// close the find handle
FindClose(findHandle);
}
}
我尚未验证此代码,显然并非所有类型都已定义,但它应该为我们指明正确的方向。
纯 C#,需要优化,但在不使用外部库或 p/invoking 的情况下会给人们一个先机。
public static class DirectoryEx
{
static char driveLetter;
static string longPath;
static List<string> directories;
static DirectoryEx()
{
longPath = String.Empty;
}
private static char GetAvailableDrive()
{
var all = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().Reverse();
var occupied = DriveInfo.GetDrives()
.OrderByDescending(d => d.Name)
.Select(d => (char)d.Name.ToUpper().First());
var free = all.Except(occupied).First();
return free;
}
public static List<string> GetDirectories(string path)
{
directories = new List<string>();
// recursive call
FindDirectories(path);
return directories;
}
static void FindDirectories(string path)
{
try
{
foreach (var directory in Directory.GetDirectories(path))
{
var di = new DirectoryInfo(directory);
if(!String.IsNullOrEmpty(longPath))
directories.Add(di.FullName.Replace(driveLetter + ":\\", longPath + "\\"));
else
directories.Add(di.FullName);
FindDirectories(di.FullName);
}
}
catch (UnauthorizedAccessException uaex) { Debug.WriteLine(uaex.Message); }
catch (PathTooLongException ptlex)
{
Debug.WriteLine(ptlex.Message);
longPath = path;
Task t = new Task(new Action(() =>
{
CreateVirtualDrive(longPath);
FindDirectories(driveLetter + ":\\");
DeleteVirtualDrive();
longPath = String.Empty;
}));
if (!String.IsNullOrEmpty(longPath))
t.RunSynchronously();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
static void CreateVirtualDrive(string path)
{
driveLetter = GetAvailableDrive();
Process.Start(new ProcessStartInfo() {
FileName = "cmd.exe",
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = String.Format("/c subst {0}: {1}", driveLetter.ToString(), path)
});
while (!DriveInfo.GetDrives().Select(d => d.Name.ToUpper().First()).Contains(driveLetter))
{
System.Threading.Thread.Sleep(1);
}
}
static void DeleteVirtualDrive()
{
Process.Start(new ProcessStartInfo()
{
FileName = "cmd.exe",
WindowStyle = ProcessWindowStyle.Hidden,
Arguments = String.Format("/c subst {0}: /D", driveLetter.ToString())
});
while (DriveInfo.GetDrives().Select(d => d.Name.ToUpper().First()).Contains(driveLetter))
{
System.Threading.Thread.Sleep(1);
}
}
}