【问题标题】:Enumerating Files Throwing Exception枚举文件抛出异常
【发布时间】:2011-12-07 02:00:00
【问题描述】:

我正在尝试使用以下代码枚举计算机上的文件,但每次遇到我无权读取的文件或目录时都会引发异常。抛出异常后有什么方法可以继续搜索吗?我知道有些人遇到过类似的问题,但除了单独检查每个文件/文件夹之外,还有其他方法吗?

try
{
    string[] files = Directory.GetFiles(@"C:\", *.*",SearchOption.AllDirectories);
    foreach (string file in files)
    {
       Console.WriteLine(file);
    }
}
catch
{
}

感谢您的帮助,因为这让我发疯了!

【问题讨论】:

    标签: c# file


    【解决方案1】:

    这里是一些基于 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;
        }
    }
    

    【讨论】:

      【解决方案2】:

      我今天遇到了同样的问题。我一起破解了以下代码。如果您想在实际产品中使用它,您可能需要改进错误处理。因为这是一个一次性脚本,所以我不太在意。

      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# 不是很有经验。我尝试将它称为一种方法,假设字符串根和字符串模式将是 args 但没有运气,我只是想用 Console.WriteLine() 将它输出到控制台,我在哪里适合它?谢谢!
      • 我希望我能给你+100!效果很好,非常感谢!
      猜你喜欢
      • 2022-07-29
      • 1970-01-01
      • 2022-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-20
      相关资源
      最近更新 更多