简体   繁体   中英

What is the equivalent of EnumerateFiles() in Net 2.0?

I'm processing a large number of files, therefore, I don't want to wait until the whole search is finished before the array is returned. So I don't want to use Directory.GetFiles()

According to this answer , I need to use EnumerateFiles() in order to get results during the search process. However, I'm using NET2.0 and this function seems to be introduced starting from NET 4.0

What is the equivalent of EnumerateFiles() in Net 2.0 ?

Any hints would be highly appreciated

What you need are the WinAPI calls for FindFirstFile and FindNextFile . Here's some code that uses the wrapped api calls.

IEnumerable<string> EnumerateFiles(string path)
{
    APIWrapper.FindData findData = new APIWrapper.FindData();

    APIWrapper.SafeFindHandle handle = APIWrapper.SafeNativeMethods.FindFirstFile(System.IO.Path.Combine(path, "*"), findData);
    if(!handle.IsInvalid && !handle.IsClosed)
    {
        yield return findData.fileName;

        while(!APIWrapper.SafeNativeMethods.FindNextFile(handle, findData))
            yield return findData.fileName;
        handle.Close();
    }
}

I just hand typed EnumerateFiles so treat it as pseudo code, but the class it relies on is production ready, this is it here

internal class APIWrapper
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal sealed class FILETIME
    {
        public int Low;
        public int High;
        public Int64 ToInt64()
        {
            Int64 h = High;

            h = h << 32;
            return h + Low;
        }
    }


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal sealed class FindData
    {
        public int fileAttributes;
        public FILETIME CreationTime;
        public FILETIME LastAccessTime;
        public FILETIME LastWriteTime;
        public int FileSizeHigh;
        public int FileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public String fileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public String alternateFileName;
    }
    internal sealed class SafeFindHandle : Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public SafeFindHandle()
            : base(true)
        {
        }

        /// <summary>
        /// Release the find handle
        /// </summary>
        /// <returns>true if the handle was released</returns>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        protected override bool ReleaseHandle()
        {
            return SafeNativeMethods.FindClose(handle);
        }
    }

    internal enum SearchOptions
    {
        NameMatch,
        LimitToDirectories,
        LimitToDevices
    }
    [SecurityPermissionAttribute(SecurityAction.Assert, UnmanagedCode = true)]
    internal static class SafeNativeMethods
    {
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        public static extern SafeFindHandle FindFirstFile(String fileName, [In, Out] FindData findFileData);

        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        public static extern SafeFindHandle FindFirstFileEx(
            String fileName,                    //__in        LPCTSTR lpFileName,
            [In] int infoLevel,                 //__in        FINDEX_INFO_LEVELS fInfoLevelId,
            [In, Out] FindData findFileData,    //__out       LPVOID lpFindFileData,
            [In, Out] SearchOptions SerchOps,             //__in        FINDEX_SEARCH_OPS fSearchOp,
            [In] int SearchFilter,              //__reserved  LPVOID lpSearchFilter,
            [In] int AdditionalFlags);          //__in        DWORD dwAdditionalFlags

        [DllImport("kernel32", CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FindNextFile(SafeFindHandle hFindFile, [In, Out] FindData lpFindFileData);

        [DllImport("kernel32", CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FindClose(IntPtr hFindFile);
    }
}

Specially added as a new answer..

Since .NET 2.0 There is IENumerable and yield keyword does Lazy Initialization/deferred execution..With these, you can get your wants.

public IEnumerable<string> GetFiles(string rootPath, string [] fileNameStartChars, string[] extensionsFilter)
        {
            FileSystemInfo[] fsi = null;
            for(int i = 0; i < fileNameStartChars.Length; i++)
            {
                for(int k = 0; k<extensionsFilter.Length; k++)
                {
                    fsi = new DirectoryInfo(rootPath).GetFileSystemInfos(fileNameStartChars[i]+extensionsFilter[k]);

                    if (fsi.Length > 0)
                    {
                        for (int j = 0; j < fsi.Length; j++)
                        {

                          /// .Name returns the filename with extension..if you need, please implement here a substring for eliminate the extension of the file
                            yield return fsi[j].Name;
                        }
                    }
                }

            }

        }

And usage :

possible filenames startsWithChar table

public string[] table = new string[]
        {
          "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
          "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
          "1","2","3","4","5","6","7","8","9","0","#","_","-",".","@","+",",","%","&","(",")","[","]","{","}","*",
          "<",">","^"," ","|",";","`"
         };

And extensions :

string[] Exts = new string[] { ".mp3", ".midi", ".wav"};

with this method, you can filter your data within small parts as such as using startswithchar filtering, so you won't get Memory problem which depends to your files count..This is the tricky part of trying to imitate .net v4's EnumerateFiles method with 100% .net v2 managed code..

     IEnumerable<string> strNumerable = GetFiles(@"D:\Music", table, Exts);


///Since its deferred execution, method didn't get any memory alloc for your data till now..Memory Alloc will start within this foreach..

            foreach (string s in strNumerable)
            {
               //do your work
            }

Since .NET 2.0 There is IENumerable and yield keyword does Lazy Initialization ..With these, you can get your wants.

With a pseudo :

public IENumerable GetFiles(string Path, string FileExtension)
{
   // Create a new IENumerable instance
   // Get FileCount with DirectoryInfo or some similar
   // Implement a for-loop with File count
   // If DirectoryFiles [ indexOfForLoop ] .Extension == FileExtension

   yield return DirectoryFiles [indexOfForLoop ]
}

In this pseudo the yield keyword take responsibility of the filtering..If filtering returns true the yield return immediately return the result to the IENumerable instance / callee..

And IEnumerable takes responsibility of Lazy Loading..

Depends to your needs, Also you can use yield break keyword in loop to not include the result..

And with a simple call :

List<string> FilesInDirectory = GetFiles( path, "*.txt").ToList();

Hope this helps..

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM