不幸的是微軟的落實GetDirectories()
極差,而不會處理有關的訪問權限IO異常。
如果你想只跳過那些你沒有訪問(如特殊Recycle Bin
文件夾,例如)目錄,那麼你必須寫自己的包裝的Windows API函數FindFirstFile()
和FindNextFile()
。
下面是一個完整的例子。如果您運行它,您會看到它列出了您的C:驅動器上可訪問的目錄的所有全部。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.Win32.SafeHandles;
namespace Demo
{
public class Program
{
private void run()
{
string root = "C:\\";
foreach (var folder in FolderEnumerator.EnumerateFoldersRecursively(root))
Console.WriteLine(folder);
}
private static void Main()
{
new Program().run();
}
}
public static class FolderEnumerator
{
public static IEnumerable<string> EnumerateFoldersRecursively(string root)
{
foreach (var folder in EnumerateFolders(root))
{
yield return folder;
foreach (var subfolder in EnumerateFoldersRecursively(folder))
yield return subfolder;
}
}
public static IEnumerable<string> EnumerateFolders(string root)
{
WIN32_FIND_DATA findData;
string spec = Path.Combine(root, "*");
using (SafeFindHandle findHandle = FindFirstFile(spec, out findData))
{
if (!findHandle.IsInvalid)
{
do
{
if ((findData.cFileName != ".") && (findData.cFileName != "..")) // Ignore special "." and ".." folders.
{
if ((findData.dwFileAttributes & FileAttributes.Directory) != 0)
{
yield return Path.Combine(root, findData.cFileName);
}
}
}
while (FindNextFile(findHandle, out findData));
}
}
}
internal sealed class SafeFindHandle: SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
public SafeFindHandle(): base(true)
{
}
protected override bool ReleaseHandle()
{
if (!IsInvalid && !IsClosed)
{
return FindClose(this);
}
return (IsInvalid || IsClosed);
}
protected override void Dispose(bool disposing)
{
if (!IsInvalid && !IsClosed)
{
FindClose(this);
}
base.Dispose(disposing);
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct FILETIME
{
public uint dwLowDateTime;
public uint dwHighDateTime;
public long ToLong()
{
return dwLowDateTime + ((long)dwHighDateTime) << 32;
}
};
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
internal struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public int nFileSizeHigh;
public int nFileSizeLow;
public int dwReserved0;
public int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_PATH)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=MAX_ALTERNATE)]
public string cAlternate;
}
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
private static extern SafeFindHandle FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FindNextFile(SafeHandle hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FindClose(SafeHandle hFindFile);
private const int MAX_PATH = 260;
private const int MAX_ALTERNATE = 14;
}
}
注:此代碼使用FindFirstFile()
和FindNextFile()
它通過的所有文件夾和文件進行迭代。上面的代碼只是忽略文件,只返回文件夾。
使用FindFirstFileEx()
並指定一個標誌只返回目錄會更有效率。我把這個改變留給讀者來說是衆所周知的。 ;)
Nitpick:你只應該忽略你想忽略的* specific *異常,例如,在你的情況下是'UnauthorizedAccessException'。否則,您可能會意外吞下其他異常,從而創建難以發現的錯誤。 – Heinzi
仍然訪問被拒絕 – user2214609
只搜索頂層目錄並執行你自己的遞歸循環 – Sayse