65.9K
CodeProject is changing. Read more.
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.06/5 (14 votes)

Jul 21, 2007

CPOL

1 min read

viewsIcon

82373

downloadIcon

1246

In C#, use Win32 API to enumerate file and directory quickly

Screenshot - enumfile.png

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 
  • FindClose 

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 object 
  • MoveNext method to search next file or sub directory
  • Current 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