枚举文件时抛出异常

8
我正在尝试使用以下代码枚举我的计算机上的文件,但每当它碰到我没有权限读取的文件或目录时,它就会抛出异常。有没有办法在抛出异常后继续搜索?我知道有些人遇到了类似的问题,但除了逐个检查每个文件/文件夹之外,还有其他方法吗?
try
{
    string[] files = Directory.GetFiles(@"C:\", "*.*",SearchOption.AllDirectories);
    foreach (string file in files)
    {
        Console.WriteLine(file);
    }
}
catch
{
}

感谢任何帮助,因为这让我发疯了!

2个回答

11
我今天也遇到了同样的问题。我拼凑出了以下代码。如果您想在真正的产品中使用它,您可能需要改进错误处理。因为这只是一个一次性脚本,所以我并不太关心它。
static IEnumerable<string> EnumerateFilesRecursive(string root,string pattern="*")
{
    var todo = new Queue<string>();
    todo.Enqueue(root);
    while (todo.Count > 0)
    {
        string dir = todo.Dequeue();
        string[] subdirs = new string[0];
        string[] files = new string[0];
        try
        {
            subdirs = Directory.GetDirectories(dir);
            files = Directory.GetFiles(dir, pattern);
        }
        catch (IOException)
        {
        }
        catch (System.UnauthorizedAccessException)
        {
        }

        foreach (string subdir in subdirs)
        {
            todo.Enqueue(subdir);
        }
        foreach (string filename in files)
        {
            yield return filename;
        }
    }
}
要使用它,你可以选择以下方法之一:
string[] files = EnumerateFilesRecursive(@"C:\").ToArray();//Note the ToArray()
foreach (string file in files)
{
   Console.WriteLine(file);
}

该方法首先枚举所有文件,将所有文件名存储在内存中,然后再显示它们。或者你可以:

IEnumerable<string> files = EnumerateFilesRecursive(@"C:\");//Note that there is NO ToArray()
foreach (string file in files)
{
   Console.WriteLine(file);
}

该方法在枚举文件时进行写入操作,因此不需要同时将所有文件名保存在内存中。


太好了,非常感谢您分享您的代码。不过我还无法让它正常工作,因为我对C#并不是很熟悉。我尝试将其作为方法调用,并假定字符串root和字符串pattern将作为参数,但没有成功。我只想通过Console.WriteLine()将其输出到控制台,请问应该在哪里添加这段代码?谢谢! - Bali C
真希望我能给你+100!那个完美地解决了问题,非常感谢你! - Bali C

0

这里是一些基于Windows API的实用代码,可以枚举文件系统条目而不会抛出/捕获异常。

示例用法:

...
foreach (var fi in Utilities.EnumerateFileSystemEntries(@"c:\windows\system32", DirectoryEnumerateOptions.Recursive))
{
    Console.WriteLine(fi.FullName);
}
...

实用类:

[Flags]
public enum DirectoryEnumerateOptions
{
    None = 0x0,
    Recursive = 0x1,
    ThrowErrors = 0x2, // if you really want it
    ExpandEnvironmentVariables = 0x4,
}

public static class Utilities
{
    public static IEnumerable<FileSystemInfo> EnumerateFileSystemEntries(string directoryPath, DirectoryEnumerateOptions options = DirectoryEnumerateOptions.None)
    {
        if (directoryPath == null)
            throw new ArgumentNullException(nameof(directoryPath));

        if (!Path.IsPathRooted(directoryPath))
        {
            directoryPath = Path.GetFullPath(directoryPath);
        }

        return EnumerateFileSystemEntriesPrivate(directoryPath, options);
    }

    private static IEnumerable<FileSystemInfo> EnumerateFileSystemEntriesPrivate(string directoryPath, DirectoryEnumerateOptions options = DirectoryEnumerateOptions.None)
    {
        if (!Directory.Exists(directoryPath))
            yield break;

        var findPath = Normalize(directoryPath, options.HasFlag(DirectoryEnumerateOptions.ExpandEnvironmentVariables));
        if (!findPath.EndsWith("*"))
        {
            findPath = Path.Combine(findPath, "*");
        }

        var h = FindFirstFile(findPath, out var data);
        if (h == INVALID_HANDLE_VALUE)
        {
            if (options.HasFlag(DirectoryEnumerateOptions.ThrowErrors))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            yield break;
        }

        if (Include(ref data))
        {
            yield return ToInfo(ref data, directoryPath);
            if (options.HasFlag(DirectoryEnumerateOptions.Recursive) && data.fileAttributes.HasFlag(FileAttributes.Directory))
            {
                foreach (var wfd in EnumerateFileSystemEntriesPrivate(Path.Combine(directoryPath, data.cFileName), options))
                {
                    yield return wfd;
                }
            }
        }
        do
        {
            if (!FindNextFile(h, out data))
            {
                if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_FILES)
                {
                    FindClose(h);
                    break;
                }
                continue;
            }

            if (Include(ref data))
            {
                yield return ToInfo(ref data, directoryPath);
                if (options.HasFlag(DirectoryEnumerateOptions.Recursive) && data.fileAttributes.HasFlag(FileAttributes.Directory))
                {
                    foreach (var wfd in EnumerateFileSystemEntriesPrivate(Path.Combine(directoryPath, data.cFileName), options))
                    {
                        yield return wfd;
                    }
                }
            }
        }
        while (true);
    }

    private static bool Include(ref WIN32_FIND_DATA data) => data.cFileName != "." && data.cFileName != "..";

    private static FileSystemInfo ToInfo(ref WIN32_FIND_DATA data, string directoryPath)
    {
        if (data.fileAttributes.HasFlag(FileAttributes.Directory))
            return new FileInfo(Path.Combine(directoryPath, data.cFileName));

        return new DirectoryInfo(Path.Combine(directoryPath, data.cFileName));
    }

    private static string Normalize(string path, bool expandEnvironmentVariables)
    {
        if (path == null)
            return null;

        string expanded;
        if (expandEnvironmentVariables)
        {
            expanded = Environment.ExpandEnvironmentVariables(path);
        }
        else
        {
            expanded = path;
        }

        if (expanded.StartsWith(_prefix))
            return expanded;

        if (expanded.StartsWith(@"\\"))
            return _uncPrefix + expanded.Substring(2);

        return _prefix + expanded;
    }

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

    [DllImport("kernel32", SetLastError = true)]
    private static extern bool FindClose(IntPtr hFindFile);

    [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

    #pragma warning disable IDE1006 // Naming Styles
    private const int ERROR_NO_MORE_FILES = 18;
    private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
    #pragma warning restore IDE1006 // Naming Styles

    private const string _prefix = @"\\?\";
    private const string _uncPrefix = _prefix + @"UNC\";

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    internal struct WIN32_FIND_DATA
    {
        public FileAttributes fileAttributes;
        public uint ftCreationTimeLow;
        public uint ftCreationTimeHigh;
        public uint ftLastAccessTimeLow;
        public uint ftLastAccessTimeHigh;
        public uint ftLastWriteTimeLow;
        public uint ftLastWriteTimeHigh;
        public uint fileSizeHigh;
        public uint fileSizeLow;
        public uint dwReserved0;
        public uint dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接