我们有时需要遍历某个目录下的文件和子目录,可以使用System.IO.DirectoryInfo.GetDirectories或GetFiles来获得目录下的所有的文件和子目录,当这个目录下的内容比较多时,这个操作就比较耗时间,有时我们仅仅需要知道某个目录下是否有子目录,这样的操作显然是浪费时间的。此时我们很容易想到三个Win32API函数 FindFirstFile,FindNextFile和FindClose。这三个API搭配使用就能遍历文件和子目录了,而且可以遍历的时候随时中止,避免无谓的操作。
C#中可以使用foreach来遍历某个序列,遍历使用的对象必须实现 System.Collections.IEnumeable接口,而内部调用的遍历器则必须实现System.Collections.IEnumerator , 为了使用方便,我们在使用FindFirstFile等API函数时封装为 IEnumerator,而且实际上是有条件封装的。
这里很多人就会提到C#调用API的执行效率问题,认为应当用C,C++调用API才是正道,使用C#调用则有些鸡肋。但在我个人编程经历中,也有不少调用API的,经验发现其实效率问题不大,可以省略。我只是做常规的运行在PC机上面的软件,CPU通常超过1GHZ,而且无需考虑高实时性和高效率。若过于考虑效率问题会加大软件开发消耗。从工程开发管理方面看是不合理的。我应当解决比较突出的效率问题,不突出的影响不大的效率问题有时间才去解决。使用C#封装Win32API必然会降低执行效率,但是封装后使用方便快捷,综合考虑认为这是正确的。
这里说一下“技术镀金”这个问题,所谓技术镀金就是开发人员在项目软件开发中过于追求技术的完美性,试图在技术上镀上一层完美的金壳,导致软件开发工作量加大,项目时间拉长,有可能导致项目的失败。我吃过“技术镀金”的苦头,现在我内心是追求完美的,但实际开发时经常有意压制追求完美的心思。
现在继续探讨封装大计,本次封装重点在于实现IEnumerator,而IEnumeable只是IEnumerator的一个包装。IEnumerator实现方法 Reset , MoveNext 和属性 Current,Reset方法用于重新设置遍历器,MoveNext用于查找下一个文件或目录,而Current返回当前文件或目录。
这个遍历器还得注意FindClose的调用,必须在遍历完毕没有找到文件或子目录后调用,若不调用该API函数则会造成内存泄漏。
根据上述设计,我写出如下代码,这段代码功能单一,希望有人能用得上
///
<summary>
///
文件或目录遍历器,本类型为 FileDirectoryEnumerator 的一个包装
///
</summary>
///
<remarks>
///
///
编写 袁永福 (
http://www.xdesigner.cn
)2006-12-8
///
///
以下代码演示使用这个文件目录遍历器
///
///
FileDirectoryEnumerable e = new FileDirectoryEnumerable();
///
e.SearchPath = @"c:\";
///
e.ReturnStringType = true ;
///
e.SearchPattern = "*.exe";
///
e.SearchDirectory = false ;
///
e.SearchFile = true;
///
foreach (object name in e)
///
{
///
System.Console.WriteLine(name);
///
}
///
System.Console.ReadLine();
///
///
</remarks>
public
class
FileDirectoryEnumerable : System.Collections.IEnumerable
{
private
bool
bolReturnStringType
=
true
;
///
<summary>
///
是否以字符串方式返回查询结果,若返回true则当前对象返回为字符串,
///
否则返回 System.IO.FileInfo或System.IO.DirectoryInfo类型
///
</summary>
public
bool
ReturnStringType
{
get
{
return
bolReturnStringType; }
set
{ bolReturnStringType
=
value; }
}
private
string
strSearchPattern
=
"
*
"
;
///
<summary>
///
文件或目录名的通配符
///
</summary>
public
string
SearchPattern
{
get
{
return
strSearchPattern; }
set
{ strSearchPattern
=
value; }
}
private
string
strSearchPath
=
null
;
///
<summary>
///
搜索路径,必须为绝对路径
///
</summary>
public
string
SearchPath
{
get
{
return
strSearchPath; }
set
{ strSearchPath
=
value; }
}
private
bool
bolSearchForFile
=
true
;
///
<summary>
///
是否查找文件
///
</summary>
public
bool
SearchForFile
{
get
{
return
bolSearchForFile; }
set
{ bolSearchForFile
=
value; }
}
private
bool
bolSearchForDirectory
=
true
;
///
<summary>
///
是否查找子目录
///
</summary>
public
bool
SearchForDirectory
{
get
{
return
bolSearchForDirectory; }
set
{ bolSearchForDirectory
=
value; }
}
private
bool
bolThrowIOException
=
true
;
///
<summary>
///
发生IO错误时是否抛出异常
///
</summary>
public
bool
ThrowIOException
{
get
{
return
this
.bolThrowIOException; }
set
{
this
.bolThrowIOException
=
value; }
}
///
<summary>
///
返回内置的文件和目录遍历器
///
</summary>
///
<returns>
遍历器对象
</returns>
public
System.Collections.IEnumerator GetEnumerator()
{
FileDirectoryEnumerator e
=
new
FileDirectoryEnumerator();
e.ReturnStringType
=
this
.bolReturnStringType;
e.SearchForDirectory
=
this
.bolSearchForDirectory;
e.SearchForFile
=
this
.bolSearchForFile;
e.SearchPath
=
this
.strSearchPath;
e.SearchPattern
=
this
.strSearchPattern;
e.ThrowIOException
=
this
.bolThrowIOException;
myList.Add(e);
return
e;
}
///
<summary>
///
关闭对象
///
</summary>
public
void
Close()
{
foreach
(FileDirectoryEnumerator e
in
myList)
{
e.Close();
}
myList.Clear();
}
private
System.Collections.ArrayList myList
=
new
System.Collections.ArrayList();
}
//
public class FileDirectoryEnumerable : System.Collections.IEnumerable
///
<summary>
///
文件和目录的遍历器
///
</summary>
///
<remarks>
本对象为Win32API函数 FindFirstFile , FindNextFile
///
和 FindClose 的一个包装
///
///
以下代码演示使用了 FileDirectoryEnumerator
///
///
FileDirectoryEnumerator e = new FileDirectoryEnumerator();
///
e.SearchPath = @"c:\";
///
e.Reset();
///
e.ReturnStringType = true ;
///
while (e.MoveNext())
///
{
///
System.Console.WriteLine
///
( e.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss")
///
+ " " + e.FileLength + " \t" + e.Name );
///
}
///
e.Close();
///
System.Console.ReadLine();
///
///
编写 袁永福 (
http://www.xdesigner.cn
)2006-12-8
</remarks>
public
class
FileDirectoryEnumerator : System.Collections.IEnumerator
{
#region
表示对象当前状态的数据和属性 **********************************
///
<summary>
///
当前对象
///
</summary>
private
object
objCurrentObject
=
null
;
private
bool
bolIsEmpty
=
false
;
///
<summary>
///
该目录为空
///
</summary>
public
bool
IsEmpty
{
get
{
return
bolIsEmpty; }
}
private
int
intSearchedCount
=
0
;
///
<summary>
///
已找到的对象的个数
///
</summary>
public
int
SearchedCount
{
get
{
return
intSearchedCount; }
}
private
bool
bolIsFile
=
true
;
///
<summary>
///
当前对象是否为文件,若为true则当前对象为文件,否则为目录
///
</summary>
public
bool
IsFile
{
get
{
return
bolIsFile; }
}
private
int
intLastErrorCode
=
0
;
///
<summary>
///
最后一次操作的Win32错误代码
///
</summary>
public
int
LastErrorCode
{
get
{
return
intLastErrorCode; }
}
///
<summary>
///
当前对象的名称
///
</summary>
public
string
Name
{
get
{
if
(
this
.objCurrentObject
!=
null
)
{
if
(objCurrentObject
is
string
)
return
(
string
)
this
.objCurrentObject;
else
return
((System.IO.FileSystemInfo)
this
.objCurrentObject).Name;
}
return
null
;
}
}
///
<summary>
///
当前对象属性
///
</summary>
public
System.IO.FileAttributes Attributes
{
get
{
return
(System.IO.FileAttributes)myData.dwFileAttributes; }
}
///
<summary>
///
当前对象创建时间
///
</summary>
public
System.DateTime CreationTime
{
get
{
long
time
=
ToLong(myData.ftCreationTime_dwHighDateTime, myData.ftCreationTime_dwLowDateTime);
System.DateTime dtm
=
System.DateTime.FromFileTimeUtc(time);
return
dtm.ToLocalTime();
}
}
///
<summary>
///
当前对象最后访问时间
///
</summary>
public
System.DateTime LastAccessTime
{
get
{
long
time
=
ToLong(myData.ftLastAccessTime_dwHighDateTime, myData.ftLastAccessTime_dwLowDateTime);
System.DateTime dtm
=
System.DateTime.FromFileTimeUtc(time);
return
dtm.ToLocalTime();
}
}
///
<summary>
///
当前对象最后保存时间
///
</summary>
public
System.DateTime LastWriteTime
{
get
{
long
time
=
ToLong(myData.ftLastWriteTime_dwHighDateTime, myData.ftLastWriteTime_dwLowDateTime);
System.DateTime dtm
=
System.DateTime.FromFileTimeUtc(time);
return
dtm.ToLocalTime();
}
}
///
<summary>
///
当前文件长度,若为当前对象为文件则返回文件长度,若当前对象为目录则返回0
///
</summary>
public
long
FileLength
{
get
{
if
(
this
.bolIsFile)
return
ToLong(myData.nFileSizeHigh, myData.nFileSizeLow);
else
return
0
;
}
}
#endregion
#region
控制对象特性的一些属性 ****************************************
private
bool
bolThrowIOException
=
true
;
///
<summary>
///
发生IO错误时是否抛出异常
///
</summary>
public
bool
ThrowIOException
{
get
{
return
this
.bolThrowIOException; }
set
{
this
.bolThrowIOException
=
value; }
}
private
bool
bolReturnStringType
=
true
;
///
<summary>
///
是否以字符串方式返回查询结果,若返回true则当前对象返回为字符串,
///
否则返回 System.IO.FileInfo或System.IO.DirectoryInfo类型
///
</summary>
public
bool
ReturnStringType
{
get
{
return
bolReturnStringType; }
set
{ bolReturnStringType
=
value; }
}
private
string
strSearchPattern
=
"
*
"
;
///
<summary>
///
要匹配的文件或目录名,支持通配符
///
</summary>
public
string
SearchPattern
{
get
{
return
strSearchPattern; }
set
{ strSearchPattern
=
value; }
}
private
string
strSearchPath
=
null
;
///
<summary>
///
搜索的父目录,必须为绝对路径,不得有通配符,该目录必须存在
///
</summary>
public
string
SearchPath
{
get
{
return
strSearchPath; }
set
{ strSearchPath
=
value; }
}
private
bool
bolSearchForFile
=
true
;
///
<summary>
///
是否查找文件
///
</summary>
public
bool
SearchForFile
{
get
{
return
bolSearchForFile; }
set
{ bolSearchForFile
=
value; }
}
private
bool
bolSearchForDirectory
=
true
;
///
<summary>
///
是否查找子目录
///
</summary>
public
bool
SearchForDirectory
{
get
{
return
bolSearchForDirectory; }
set
{ bolSearchForDirectory
=
value; }
}
#endregion
///
<summary>
///
关闭对象,停止搜索
///
</summary>
public
void
Close()
{
this
.CloseHandler();
}
#region
IEnumerator 成员 **********************************************
///
<summary>
///
返回当前对象
///
</summary>
public
object
Current
{
get
{
return
objCurrentObject ; }
}
///
<summary>
///
找到下一个文件或目录
///
</summary>
///
<returns>
操作是否成功
</returns>
public
bool
MoveNext()
{
bool
success
=
false
;
while
(
true
)
{
if
(
this
.bolStartSearchFlag)
success
=
this
.SearchNext();
else
success
=
this
.StartSearch();
if
(success)
{
if
(
this
.UpdateCurrentObject())
return
true
;
}
else
{
this
.objCurrentObject
=
null
;
return
false
;
}
}
}
///
<summary>
///
重新设置对象
///
</summary>
public
void
Reset()
{
if
(
this
.strSearchPath
==
null
)
throw
new
System.ArgumentNullException(
"
SearchPath can not null
"
);
if
(
this
.strSearchPattern
==
null
||
this
.strSearchPattern.Length
==
0
)
this
.strSearchPattern
=
"
*
"
;
this
.intSearchedCount
=
0
;
this
.objCurrentObject
=
null
;
this
.CloseHandler();
this
.bolStartSearchFlag
=
false
;
this
.bolIsEmpty
=
false
;
this
.intLastErrorCode
=
0
;
}
#endregion
#region
声明WIN32API函数以及结构 **************************************
[Serializable,
System.Runtime.InteropServices.StructLayout
(System.Runtime.InteropServices.LayoutKind.Sequential,
CharSet
=
System.Runtime.InteropServices.CharSet.Auto
),
System.Runtime.InteropServices.BestFitMapping(
false
)]
private
struct
WIN32_FIND_DATA
{
public
int
dwFileAttributes;
public
int
ftCreationTime_dwLowDateTime;
public
int
ftCreationTime_dwHighDateTime;
public
int
ftLastAccessTime_dwLowDateTime;
public
int
ftLastAccessTime_dwHighDateTime;
public
int
ftLastWriteTime_dwLowDateTime;
public
int
ftLastWriteTime_dwHighDateTime;
public
int
nFileSizeHigh;
public
int
nFileSizeLow;
public
int
dwReserved0;
public
int
dwReserved1;
[System.Runtime.InteropServices.MarshalAs
(System.Runtime.InteropServices.UnmanagedType.ByValTStr,
SizeConst
=
260
)]
public
string
cFileName;
[System.Runtime.InteropServices.MarshalAs
(System.Runtime.InteropServices.UnmanagedType.ByValTStr,
SizeConst
=
14
)]
public
string
cAlternateFileName;
}
[System.Runtime.InteropServices.DllImport
(
"
kernel32.dll
"
,
CharSet
=
System.Runtime.InteropServices.CharSet.Auto,
SetLastError
=
true
)]
private
static
extern
IntPtr FindFirstFile(
string
pFileName,
ref
WIN32_FIND_DATA pFindFileData);
[System.Runtime.InteropServices.DllImport
(
"
kernel32.dll
"
,
CharSet
=
System.Runtime.InteropServices.CharSet.Auto,
SetLastError
=
true
)]
private
static
extern
bool
FindNextFile(IntPtr hndFindFile,
ref
WIN32_FIND_DATA lpFindFileData);
[System.Runtime.InteropServices.DllImport(
"
kernel32.dll
"
, SetLastError
=
true
)]
private
static
extern
bool
FindClose(IntPtr hndFindFile);
private
static
long
ToLong(
int
height ,
int
low)
{
long
v
=
(
uint
) height ;
v
=
v
<<
0x20
;
v
=
v
|
( (
uint
)low );
return
v;
}
private
static
void
WinIOError(
int
errorCode,
string
str)
{
switch
(errorCode)
{
case
80
:
throw
new
System.IO.IOException(
"
IO_FileExists :
"
+
str);
case
0x57
:
throw
new
System.IO.IOException(
"
IOError:
"
+
MakeHRFromErrorCode(errorCode));
case
0xce
:
throw
new
System.IO.PathTooLongException(
"
PathTooLong:
"
+
str );
case
2
:
throw
new
System.IO.FileNotFoundException(
"
FileNotFound:
"
+
str);
case
3
:
throw
new
System.IO.DirectoryNotFoundException(
"
PathNotFound:
"
+
str);
case
5
:
throw
new
UnauthorizedAccessException(
"
UnauthorizedAccess:
"
+
str);
case
0x20
:
throw
new
System.IO.IOException(
"
IO_SharingViolation:
"
+
str);
}
throw
new
System.IO.IOException(
"
IOError:
"
+
MakeHRFromErrorCode(errorCode));
}
private
static
int
MakeHRFromErrorCode(
int
errorCode)
{
return
(
-
2147024896
|
errorCode);
}
#endregion
#region
内部代码群 ****************************************************
private
static
readonly
IntPtr INVALID_HANDLE_VALUE
=
new
IntPtr(
-
1
);
///
<summary>
///
查找处理的底层句柄
///
</summary>
private
System.IntPtr intSearchHandler
=
INVALID_HANDLE_VALUE;
private
WIN32_FIND_DATA myData
=
new
WIN32_FIND_DATA();
///
<summary>
///
开始搜索标志
///
</summary>
private
bool
bolStartSearchFlag
=
false
;
///
<summary>
///
关闭内部句柄
///
</summary>
private
void
CloseHandler()
{
if
(
this
.intSearchHandler
!=
INVALID_HANDLE_VALUE)
{
FindClose(
this
.intSearchHandler);
this
.intSearchHandler
=
INVALID_HANDLE_VALUE;
}
}
///
<summary>
///
开始搜索
///
</summary>
///
<returns>
操作是否成功
</returns>
private
bool
StartSearch()
{
bolStartSearchFlag
=
true
;
bolIsEmpty
=
false
;
objCurrentObject
=
null
;
intLastErrorCode
=
0
;
string
strPath
=
System.IO.Path.Combine(strSearchPath,
this
.strSearchPattern);
this
.CloseHandler();
intSearchHandler
=
FindFirstFile(strPath,
ref
myData);
if
(intSearchHandler
==
INVALID_HANDLE_VALUE)
{
intLastErrorCode
=
System.Runtime.InteropServices.Marshal.GetLastWin32Error();
if
(intLastErrorCode
==
2
)
{
bolIsEmpty
=
true
;
return
false
;
}
if
(
this
.bolThrowIOException )
WinIOError( intLastErrorCode , strSearchPath);
else
return
false
;
}
return
true
;
}
///
<summary>
///
搜索下一个
///
</summary>
///
<returns>
操作是否成功
</returns>
private
bool
SearchNext()
{
if
(bolStartSearchFlag
==
false
)
return
false
;
if
(bolIsEmpty)
return
false
;
if
(intSearchHandler
==
INVALID_HANDLE_VALUE)
return
false
;
intLastErrorCode
=
0
;
if
(FindNextFile(intSearchHandler,
ref
myData)
==
false
)
{
intLastErrorCode
=
System.Runtime.InteropServices.Marshal.GetLastWin32Error();
this
.CloseHandler();
if
(intLastErrorCode
!=
0
&&
intLastErrorCode
!=
0x12
)
{
if
(
this
.bolThrowIOException)
WinIOError(intLastErrorCode , strSearchPath);
else
return
false
;
}
return
false
;
}
return
true
;
}
//
private bool SearchNext()
///
<summary>
///
更新当前对象
///
</summary>
///
<returns>
操作是否成功
</returns>
private
bool
UpdateCurrentObject()
{
if
(intSearchHandler
==
INVALID_HANDLE_VALUE)
return
false
;
bool
Result
=
false
;
this
.objCurrentObject
=
null
;
if
((myData.dwFileAttributes
&
0x10
)
==
0
)
{
//
当前对象为文件
this
.bolIsFile
=
true
;
if
(
this
.bolSearchForFile)
Result
=
true
;
}
else
{
//
当前对象为目录
this
.bolIsFile
=
false
;
if
(
this
.bolSearchForDirectory)
{
if
(myData.cFileName
==
"
.
"
||
myData.cFileName
==
"
..
"
)
Result
=
false
;
else
Result
=
true
;
}
}
if
(Result)
{
if
(
this
.bolReturnStringType)
this
.objCurrentObject
=
myData.cFileName;
else
{
string
p
=
System.IO.Path.Combine(
this
.strSearchPath, myData.cFileName);
if
(
this
.bolIsFile)
{
this
.objCurrentObject
=
new
System.IO.FileInfo(p);
}
else
{
this
.objCurrentObject
=
new
System.IO.DirectoryInfo(p);
}
}
this
.intSearchedCount
++
;
}
return
Result;
}
//
private bool UpdateCurrentObject()
#endregion
}
//
public class FileDirectoryEnumerator : System.Collections.IEnumerator
袁永福 ( http://www.xdesigner.cn )2007