[C#]使用FindFirstFile、FindNextFile API實做EnumerateFiles
.NET 4.0開始Directory類別新增了EnumerateFiles函式,該函式能提供較有效率的方式找尋檔案,不會等到整個搜尋動作完成才回傳。在.NET 4.0以前我們則可以用FindFirstFile、FindNextFile這幾個API來達到類似的效果。
實做起來就像下面這樣,有需要的自行取用:
[DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern IntPtr FindFirstFile(string pFileName, ref WIN32_FIND_DATA pFindFileData);
[DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern bool FindNextFile(IntPtr hndFindFile, ref WIN32_FIND_DATA lpFindFileData);
[DllImport( "kernel32.dll" , SetLastError = true )]
private static extern bool FindClose(IntPtr hndFindFile);
[Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto), BestFitMapping(false)]
internal struct WIN32_FIND_DATA
{
public FileAttributes dwFileAttributes;
public uint ftCreationTime_dwLowDateTime;
public uint ftCreationTime_dwHighDateTime;
public uint ftLastAccessTime_dwLowDateTime;
public uint ftLastAccessTime_dwHighDateTime;
public uint ftLastWriteTime_dwLowDateTime;
public uint ftLastWriteTime_dwHighDateTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public int dwReserved0;
public int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
public IEnumerable<string> EnumerateFiles(string path, string searchPattern = "*.*", SearchOption searchOption = SearchOption.AllDirectories)
{
IntPtr hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA FindFileData = default(WIN32_FIND_DATA);
hFind = FindFirstFile(Path.Combine(path, searchPattern), ref FindFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
do
{
if (FindFileData.cFileName.Equals(@".") || FindFileData.cFileName.Equals(@".."))
continue;
if (searchOption == SearchOption.AllDirectories && ((FindFileData.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory))
{
foreach (var file in EnumerateFiles(Path.Combine(path, FindFileData.cFileName)))
yield return file;
}
else
{
yield return Path.Combine(path, FindFileData.cFileName);
}
}
while (FindNextFile(hFind, ref FindFileData));
}
FindClose(hFind);
}