In C#, Use Win32 API to Enumerate File and Directory Quickly






3.06/5 (14 votes)
In C#, use Win32 API to enumerate file and directory quickly

Introduction
This is an article about using Win32 API in C# to enumerate file and directory quickly.
Background
Sometimes we need to enumerate file and especially sub directory. We can use System.IO.DirectoryInfo.GetDirectories
or GetFiles
to get all file and sub directories. When the father directory has many files and sub directories, this operation is slow and out of control. Sometimes we just want to know if there are some special kind of file or sub directories in a directory. Obviously the System.IO.DirectoryInfo.GetDirectories
/GetFiles
is inefficient and a waste of time.
So we think about 3 Win32 APIs:
FindFirstFile
FindNextFile
FindC
lose
Using these 3 Win32 APIs, we can enumerate file and sub directory, efficiently and in a controllable manner.
Using the Code
In C#, we can use the foreach
expression to enumerate some kind of serial value. The object which we use for foreach
expression must implement Interface System.Collection.IEnumerable
. This object contains an enumerator which implements Interface System.Collection.IEnumerator
. For friendly use, we package the 3 APIs as IEnumerator
.
IEnumerator
has 3 members:
Reset
method to initialize objectMoveNext
method to search next file or sub directoryCurrent
property returns the file or sub directory that is found
After enumerating, object must call FindClose
to free resources. Then I wrote a class name FileDirectoryEnumerable
. You can use this class as follows:
string strPath = System.Environment.GetFolderPath
( System.Environment.SpecialFolder.System );
FileDirectoryEnumerable myEnum = new FileDirectoryEnumerable();
myEnum.SearchPath = strPath ;
myEnum.SearchPattern = "*.exe";
myEnum.SearchForFile = true;
myEnum.SearchForDirectory = false;
myEnum.ReturnStringType = true;
System.Console.WriteLine("----------- search EXE file name ------------------");
int iCount = 0 ;
foreach( string strFileName in myEnum )
{
System.Console.WriteLine( strFileName );
// LOOK ! , you can interrupt freely
if( ( iCount ++ ) > 10)
break;
}
System.Console.WriteLine("-------------- search DLL file name and length ----------");
myEnum.ReturnStringType = false;
myEnum.SearchPattern = "*.dll";
iCount = 0 ;
foreach( System.IO.FileInfo file in myEnum )
{
System.Console.WriteLine( file.Name + "\tLength:" + file.Length + " bytes ");
// LOOK ! , you can interrupt freely
if( ( iCount ++ ) > 10 )
break;
}
myEnum.Close();
System.Console.WriteLine("------------- Press Enter to exit ------------------");
System.Console.ReadLine();
The source code of FileDirectoryEnumerable
is:
using System;
namespace EnumerateFile
{
/// <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
}
History
- 21st July, 2007: Initial post