Click here to Skip to main content
Licence CPOL
First Posted 21 Jul 2007
Views 26,100
Downloads 261
Bookmarked 24 times

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

By fu yuans | 21 Jul 2007
In C#, use Win32 API to enumerate file and directory quickly
3 votes, 25.0%
1

2
2 votes, 16.7%
3
1 vote, 8.3%
4
6 votes, 50.0%
5
2.86/5 - 12 votes
μ 2.86, σa 3.03 [?]
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)

About the Author

fu yuans

Web Developer
sinosoft
China China

Member
yfyuan of Sinosoft , come from CHINA , 2008 Microsoft MVP,Use GDI+,XML/XSLT, site:http://www.sinoreport.net/

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralSystem_IO error PinmemberThere is always the way to do it, but I don't know19:43 18 Oct '07  
GeneralThe comments rock PinmemberEvgenyAronov1:45 23 Jul '07  
GeneralRe: The comments rock PinmemberRoland Lee6:49 25 Jul '07  
GeneralThis is not needed. PinmemberJoseph Guadagno19:38 21 Jul '07  
GeneralRe: This is not needed. Pinmemberyfyuan21:08 21 Jul '07  
GeneralRe: This is not needed. PinmemberRi Qen-Sin4:15 22 Jul '07  
GeneralRe: This is not needed. Pinmemberyfyuan4:45 22 Jul '07  
GeneralRe: This is not needed. PinmemberWilliam H Gates III22:42 24 Jul '08  
GeneralRe: This is absolutely needed. Pinmemberseriousconsult7:08 6 Feb '09  
GeneralRe: This is absolutely needed. Pinmemberhussein kaddoura12:03 16 Jun '10  
GeneralRe: This is absolutely needed. PinmemberMember 119396911:12 16 Sep '10  
GeneralRe: This is absolutely needed. Pinmember_henke_2:15 20 Apr '11  

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.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120210.1 | Last Updated 21 Jul 2007
Article Copyright 2007 by fu yuans
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid