Перебор файлов с использованием FindFirstFile/FindNextFile и итераторов C# 2.0
Демонстрирует использование итераторов C# 2.0. В том числе такие не тривиальные вещи как обработка исключений внутри итераторов и рекурсивные итераторы.
Сразу предупреждаю. Код не из рабочего проекта, а так... тест написанный под впечатлением от этогоАвтор: Sinclair
Дата: 26.09.05
сообщения и вообще той задолбавшей темы.
Так как код писался параллельно процессу отладки генератора архива для RSDN CD, да еще и в 9 утра после бессонной ночи, притензии по качеству кода и темблее орфографии не приниаются . Так что незабудьте прочесть последнюю фразу в этом сообщении.
Итак... к делу!
Тест:
using System;
class Program
{
static void Main(string[] args)
{
foreach (string dir in System.IO.RsdnDirectory.GetAllDirectories(@"..\..\.."))
{
Console.WriteLine("Dir: '" + dir + "' contains:");
foreach (string file in System.IO.RsdnDirectory.GetFilse(dir))
Console.WriteLine("\t" + file);
}
}
}
Выводит:
Dir: '..\..\..\FindFirstFile' contains:
FindFirstFile.csproj
FindFirstFile.csproj.user
Program.cs
Dir: '..\..\..\FindFirstFile\bin' contains:
Dir: '..\..\..\FindFirstFile\bin\Debug' contains:
FindFirstFile.exe
FindFirstFile.pdb
Dir: '..\..\..\FindFirstFile\bin\Release' contains:
Dir: '..\..\..\FindFirstFile\obj' contains:
FindFirstFile.csproj.FileList.txt
Dir: '..\..\..\FindFirstFile\obj\Debug' contains:
FindFirstFile.exe
FindFirstFile.pdb
Dir: '..\..\..\FindFirstFile\obj\Debug\Refactor' contains:
Dir: '..\..\..\FindFirstFile\obj\Debug\TempPE' contains:
Dir: '..\..\..\FindFirstFile\obj\Release' contains:
Dir: '..\..\..\FindFirstFile\Properties' contains:
AssemblyInfo.cs
Исходник:
namespace System.IO
{
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
using System.ComponentModel;
class RsdnDirectory
{
/// <summary>
/// Формирует путь требуемый функцией FindFirstFile.
/// </summary>
private static string MakePath(string path)
{
return Path.Combine(path, "*");
}
/// <summary>
/// Возвращает список файлов или каталогов находящихся по заданному пути.
/// </summary>
/// <param name="path">Путь для которого нужно возвратать список.</param>
/// <param name="isGetDirs">
/// Если true - функция возвращает список каталогов, иначе файлов.
/// </param>
/// <returns>Список файлов или каталогов.</returns>
private static IEnumerable<string> GetInternal(string path, bool isGetDirs)
{
// Структура в которую функции FindFirstFile и FindNextFileвозвращают
// информацию о текущем файле.
WIN32_FIND_DATA findData;
// Получаем информацию о текущем файле и хэндл перечислителя виндовс.
// Этот хэндл требуется передавать функции FindNextFile для плучения
// следующих файлов.
IntPtr findHandle = FindFirstFile(MakePath(path), out findData);
// Хреновый хэндл говорит, о том, что произошел облом. Следовательно
// нужно вынуть информацию об ошибке и перепаковать ее в исключение.
if (findHandle == INVALID_HANDLE_VALUE)
throw new Win32Exception(Marshal.GetLastWin32Error());
try
{
do
if (isGetDirs
? (findData.dwFileAttributes & FileAttributes.Directory) != 0
: (findData.dwFileAttributes & FileAttributes.Directory) == 0)
yield return findData.cFileName;
while (FindNextFile(findHandle, out findData));
}
finally
{
FindClose(findHandle);
}
}
/// <summary>
/// Возвращает список файлов для некоторого пути.
/// </summary>
/// <param name="path">
/// Каталог для которого нужно получить список файлов.
/// </param>
/// <returns>Список файлов каталога.</returns>
public static IEnumerable<string> GetFilse(string path)
{
return GetInternal(path, false);
}
/// <summary>
/// Возвращает список каталогов для некоторого пути. Функция не перебирает
/// вложенные подкаталоги!
/// </summary>
/// <param name="path">
/// Каталог для которого нужно получить список подкаталогов.
/// </param>
/// <returns>Список файлов каталога.</returns>
public static IEnumerable<string> GetDirectories(string path)
{
return GetInternal(path, true);
}
/// <summary>
/// Функция возвращает список относительных путей ко всем подкаталогам
/// (в том числе и вложенным) заданного пути.
/// </summary>
/// <param name="path">Путь для которого унжно получить подкаталоги.</param>
/// <returns>Список подкатлогов.</returns>
public static IEnumerable<string> GetAllDirectories(string path)
{
// Сначала перебираем подкаталоги первого уровня вложенности...
foreach (string subDir in GetDirectories(path))
{
// игнорируем имя текущего каталога и родительского.
if (subDir == ".." || subDir == ".")
continue;
// Комбинируем базовый путь и имя подкаталога.
string relativePath = Path.Combine(path, subDir);
// возвращам пользователю относительный путь.
yield return relativePath;
// Создаем, рекурсивно, итератор для каждого подкаталога и...
// возвращаем каждый его элемент в качестве элементов текущего итератоа.
// Этот прием позволяет обойти ограничение итераторов C# 2.0 связанное
// с нвозможностью вызовов "yield return" из функций вызваемых из
// функции итератора. К сожалению это приводит к созданию временного
// вложенного итератора на каждом шаге рекурсии, но затраты на создание
// такого объекта относительно не велики, а удобство очень даже ощутимо.
foreach (string subDir2 in GetAllDirectories(relativePath))
yield return subDir2;
}
}
#region Импорт из kernel32
private const int MAX_PATH = 260;
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
[BestFitMapping(false)]
private 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 = 14)]
public string cAlternate;
}
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr FindFirstFile(string lpFileName,
out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool FindNextFile(IntPtr hFindFile,
out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool FindClose(IntPtr hFindFile);
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
#endregion
}
}
До кондиции довести напильником.... << RSDN@Home 1.2.0 alpha rev. 618>>