1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
//------------------------------------------------------------------------------
// <copyright file="FileEnumerator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
/*
* FileEnumerator class
*
* Copyright (c) 2003 Microsoft Corporation
*
* Class to efficiently enumerate the files in a directory. The only thing the framework provides
* to do this is Directory.GetFiles(), which is unusable on large directories because it returns an
* array containing all the file names at once (huge memory allocation).
*
* An efficient alternative is to use FindFirstFile/FindNextFile, which works but requires a lot
* more code. Also, it makes our code base harder to port to non-windows platforms.
*
* This FileEnumerator class solves both problem, by providing a simple and efficient wrapper.
* By working with a single object, it is almost as efficient as calling FindFirstFile/FindNextFile,
* but is much easier to use. e.g. instead of:
*
* UnsafeNativeMethods.WIN32_FIND_DATA wfd;
* IntPtr hFindFile = UnsafeNativeMethods.FindFirstFile(physicalDir + @"\*.*", out wfd);
*
* if (hFindFile == INVALID_HANDLE_VALUE)
* return;
*
* try {
* for (bool more=true; more; more=UnsafeNativeMethods.FindNextFile(hFindFile, out wfd)) {
*
* // Skip false directories
* if (wfd.cFileName == "." || wfd.cFileName == "..")
* continue;
*
* string fullPath = Path.Combine(physicalDir, wfd.cFileName);
*
* ProcessFile(fullPath);
* }
* }
* finally {
* UnsafeNativeMethods.FindClose(hFindFile);
* }
*
* we can simply write
*
* foreach (FileData fileData in FileEnumerator.Create(physicalDir)) {
* ProcessFile(fileData.FullName);
* }
*/
namespace System.Web.Util {
using System.IO;
using System.Collections;
/*
* This is a somewhat artificial base class for FileEnumerator. The main reason
* for it is to allow user code to be more readable, by looking like:
* foreach (FileData fileData in FileEnumerator.Create(path)) { ... }
* instead of
* foreach (FileEnumerator fileData in FileEnumerator.Create(path)) { ... }
*/
internal abstract class FileData {
protected string _path;
protected UnsafeNativeMethods.WIN32_FIND_DATA _wfd;
internal string Name {
get { return _wfd.cFileName; }
}
internal string FullName {
get { return _path + @"\" + _wfd.cFileName; }
}
internal bool IsDirectory {
get { return (_wfd.dwFileAttributes & UnsafeNativeMethods.FILE_ATTRIBUTE_DIRECTORY) != 0; }
}
internal bool IsHidden {
get { return (_wfd.dwFileAttributes & UnsafeNativeMethods.FILE_ATTRIBUTE_HIDDEN) != 0; }
}
internal FindFileData GetFindFileData() {
return new FindFileData(ref _wfd);
}
}
internal class FileEnumerator: FileData, IEnumerable, IEnumerator, IDisposable {
private IntPtr _hFindFile = UnsafeNativeMethods.INVALID_HANDLE_VALUE;
internal static FileEnumerator Create(string path) {
return new FileEnumerator(path);
}
private FileEnumerator(string path) {
_path = Path.GetFullPath(path);
}
~FileEnumerator() {
((IDisposable)this).Dispose();
}
// Should the current file be excluded from the enumeration
private bool SkipCurrent() {
// Skip false directories
if (_wfd.cFileName == "." || _wfd.cFileName == "..")
return true;
return false;
}
// We just return ourselves for the enumerator, to avoid creating a new object
IEnumerator IEnumerable.GetEnumerator() {
return this;
}
bool IEnumerator.MoveNext() {
for (;;) {
if (_hFindFile == UnsafeNativeMethods.INVALID_HANDLE_VALUE) {
_hFindFile = UnsafeNativeMethods.FindFirstFile(_path + @"\*.*", out _wfd);
// Empty enumeration case
if (_hFindFile == UnsafeNativeMethods.INVALID_HANDLE_VALUE)
return false;
}
else {
bool hasMoreFiles = UnsafeNativeMethods.FindNextFile(_hFindFile, out _wfd);
if (!hasMoreFiles)
return false;
}
if (!SkipCurrent())
return true;
}
}
// The current object of the enumeration is always ourselves. No new object created.
object IEnumerator.Current {
get { return this; }
}
void IEnumerator.Reset() {
// We don't support reset, though it would be easy to add if needed
throw new InvalidOperationException();
}
void IDisposable.Dispose() {
if (_hFindFile != UnsafeNativeMethods.INVALID_HANDLE_VALUE) {
UnsafeNativeMethods.FindClose(_hFindFile);
_hFindFile = UnsafeNativeMethods.INVALID_HANDLE_VALUE;
}
System.GC.SuppressFinalize(this);
}
}
}
|