Click here to Skip to main content
11,632,260 members (86,486 online)
Click here to Skip to main content

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

, 21 Jul 2007 CPOL 43.3K 790 28
Rate this:
Please Sign up or sign in to vote.
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

yuan yong fu
Web Developer duchang soft
China China
yuan yong fu of duchang soft , come from CHINA , 2008 Microsoft MVP,Use GDI+,XML/XSLT, site:http://www.cnblogs.com/xdesigner/

You may also be interested in...

Comments and Discussions

 
QuestionGreat! Pin
Daniel Leykauf15-Jun-14 12:32
memberDaniel Leykauf15-Jun-14 12:32 
Questionthank you dude Pin
Member 661742121-Dec-12 19:53
memberMember 661742121-Dec-12 19:53 
GeneralYou're kidding me,right? Pin
PMBottas26-Jul-12 16:13
memberPMBottas26-Jul-12 16:13 
GeneralSystem_IO error Pin
There is always the way to do it, but I don't know18-Oct-07 18:43
memberThere is always the way to do it, but I don't know18-Oct-07 18:43 
GeneralThe comments rock Pin
EvgenyAronov23-Jul-07 0:45
memberEvgenyAronov23-Jul-07 0:45 
GeneralRe: The comments rock Pin
Roland Lee25-Jul-07 5:49
memberRoland Lee25-Jul-07 5:49 
GeneralThis is not needed. Pin
Joseph Guadagno21-Jul-07 18:38
memberJoseph Guadagno21-Jul-07 18:38 
GeneralRe: This is not needed. Pin
yfyuan21-Jul-07 20:08
memberyfyuan21-Jul-07 20:08 
GeneralRe: This is not needed. Pin
Ri Qen-Sin22-Jul-07 3:15
memberRi Qen-Sin22-Jul-07 3:15 
GeneralRe: This is not needed. Pin
yfyuan22-Jul-07 3:45
memberyfyuan22-Jul-07 3:45 
GeneralRe: This is not needed. Pin
William H Gates III24-Jul-08 21:42
memberWilliam H Gates III24-Jul-08 21:42 
GeneralRe: This is absolutely needed. Pin
seriousconsult6-Feb-09 6:08
memberseriousconsult6-Feb-09 6:08 
GeneralRe: This is absolutely needed. Pin
hussein kaddoura16-Jun-10 11:03
memberhussein kaddoura16-Jun-10 11:03 
GeneralRe: This is absolutely needed. Pin
Member 119396916-Sep-10 10:12
memberMember 119396916-Sep-10 10:12 
GeneralRe: This is absolutely needed. Pin
_henke_20-Apr-11 1:15
member_henke_20-Apr-11 1:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150728.1 | Last Updated 21 Jul 2007
Article Copyright 2007 by yuan yong fu
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid