Member 12524921 Ответов: 1

Как сделать поиск быстрее? С#


- Привет! Кто-нибудь может сказать, как сделать этот код быстрее?
Этот поиск занимает слишком много времени... 700 000 файлов за 15 минут...
Я буду благодарен за каждую микрооптимизацию!
Спасибо!

Search Class:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Runtime.InteropServices;

    public class FindFile
    {
        private static readonly char[] InvalidFilePathChars;
        private static readonly char[] InvalidFilePatternChars;
        static FindFile()
        {
            InvalidFilePathChars = Path.GetInvalidPathChars();
            List<char> set = new List<char>(Path.GetInvalidFileNameChars());
            set.Remove('*');
            set.Remove('?');
            InvalidFilePatternChars = set.ToArray();
        }
        // Enumerates the files directly in the directory specified
        public static void FilesIn(string directory, Action<FileFoundEventArgs> e)
        {
            FindFile ff = new FindFile(directory, STAR, false, false, true);
            ff.FileFound = (o, a) => e(a);
            ff.Find();
        }
        //Enumerates the folders directly in the directory specified
        public static void FoldersIn(string directory, Action<FileFoundEventArgs> e)
        {
            FindFile ff = new FindFile(directory, STAR, false, true, false);
            ff.FileFound = (o, a) => e(a);
            ff.Find();
        }
        //Enumerates the files and folders directly in the directory specified
        public static void FilesAndFoldersIn(string directory, Action<FileFoundEventArgs> e)
        {
            FindFile ff = new FindFile(directory, STAR, false, true, true);
            ff.FileFound = (o, a) => e(a);
            ff.Find();
        }
        //Enumerates the files anywhere under the directory specified
        public static void AllFilesIn(string directory, Action<FileFoundEventArgs> e)
        {
            FindFile ff = new FindFile(directory, STAR, true, false, true);
            ff.FileFound = (o, a) => e(a);
            ff.Find();
        }
        //Enumerates the folders anywhere under the directory specified
        public static void AllFoldersIn(string directory, Action<FileFoundEventArgs> e)
        {
            FindFile ff = new FindFile(directory, STAR, true, true, false);
            ff.FileFound = (o, a) => e(a);
            ff.Find();
        }
        //Enumerates the files and folders anywhere under the directory specified
        public static void AllFilesAndFoldersIn(string directory, Action<FileFoundEventArgs> e)
        {
            FindFile ff = new FindFile(directory, STAR, true, true, true);
            ff.FileFound = (o, a) => e(a);
            ff.Find();
        }
        #region Kernel32
        internal static class Kernel32
        {
            internal const int MAX_PATH = 260;
            internal const int MAX_ALTERNATE = 14;
            internal const int ERROR_FILE_NOT_FOUND = 2;
            internal const int ERROR_PATH_NOT_FOUND = 3;
            internal const int ERROR_ACCESS_DENIED = 5;

            [StructLayout(LayoutKind.Sequential)]
            public struct FILETIME
            {
                public uint dwLowDateTime;
                public uint dwHighDateTime;

                public DateTime ToDateTimeUtc()
                {
                    return DateTime.FromFileTimeUtc(dwLowDateTime | ((long)dwHighDateTime << 32));
                }
            };

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            public struct WIN32_FIND_DATA
            {
                public FileAttributes dwFileAttributes;
                public FILETIME ftCreationTime;
                public FILETIME ftLastAccessTime;
                public FILETIME ftLastWriteTime;
                public uint nFileSizeHigh; //changed all to uint from int, otherwise you run into unexpected overflow
                public uint nFileSizeLow;
                private uint dwReserved0;
                private uint dwReserved1;

                [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_PATH)]
                public char[] cFileName;

                [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_ALTERNATE)]
                private char[] cAlternateFileName;

                public bool IgnoredByName
                {
                    get
                    {
                        return
                            (cFileName[0] == ZERO) ||
                            (cFileName[0] == '.' && cFileName[1] == ZERO) ||
                            (cFileName[0] == '.' && cFileName[1] == '.' && cFileName[2] == ZERO);
                    }
                }
            }

            public enum FINDEX_INFO_LEVELS
            {
                FindExInfoStandard = 0,
                FindExInfoBasic = 1
            }

            public enum FINDEX_SEARCH_OPS
            {
                FindExSearchNameMatch = 0,
                FindExSearchLimitToDirectories = 1,
                FindExSearchLimitToDevices = 2
            }

            [Flags]
            public enum FINDEX_ADDITIONAL_FLAGS
            {
                FindFirstExCaseSensitive = 1,
                FindFirstExLargeFetch = 2,
            }

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr FindFirstFileEx(
                IntPtr lpFileName,
                FINDEX_INFO_LEVELS fInfoLevelId,
                out WIN32_FIND_DATA lpFindFileData,
                FINDEX_SEARCH_OPS fSearchOp,
                IntPtr lpSearchFilter,
                FINDEX_ADDITIONAL_FLAGS dwAdditionalFlags);

            [DllImport("kernel32", CharSet = CharSet.Unicode)]
            public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

            [DllImport("kernel32.dll")]
            public static extern bool FindClose(IntPtr hFindFile);
        }
        #endregion
 
        public struct Info
        {
            //Returns the parent folder's full path
            public string ParentPath { get { return Path.GetDirectoryName(FullPath); } }
            //Gets or sets the full path of the file or folder
            public string FullPath { get; set; }
            //Returns the file or folder name (with extension)
            public string Name { get { return Path.GetFileName(FullPath); } }
            //Returns the extenion or String.Empty
            public string Extension { get { return Path.GetExtension(FullPath); } }
            //Returns the UNC path to the parent folder
            public string ParentPathUnc { get { return (FullPath.StartsWith(@"\\")) ? ParentPath : (UncPrefix + ParentPath); } }
            //Returns the UNC path to the file or folder
            public string FullPathUnc { get { return (FullPath.StartsWith(@"\\")) ? FullPath : (UncPrefix + FullPath); } }
            //Gets or sets the length in bytes
            public long Length { get; set; }
            //Gets or sets the file or folder attributes
            public FileAttributes Attributes { get; set; }
            //Gets or sets the file or folder CreationTime in Utc
            public DateTime CreationTimeUtc { get; set; }
            //Gets or sets the file or folder LastAccessTime in Utc
            public DateTime LastAccessTimeUtc { get; set; }
            //Gets or sets the file or folder LastWriteTime in Utc
            public DateTime LastWriteTimeUtc { get; set; }
        }

        internal class Win32FindData
        {
            public char[] Buffer;
            public IntPtr BufferAddress;
            public Kernel32.WIN32_FIND_DATA Value;
        }

        // Provides access to the file or folder information durring enumeration, DO NOT keep a reference to this
        // class as it's meaning will change durring enumeration.

        public sealed class FileFoundEventArgs : EventArgs
        {
            private readonly Win32FindData _ff;
            private int _uncPrefixLength;
            private int _parentNameLength;
            private int _itemNameLength;
            private bool _cancelEnumeration;

            internal FileFoundEventArgs(Win32FindData ff)
            {
                _ff = ff;
            }

            internal void SetNameOffsets(int uncPrefixLength, int parentIx, int itemIx)
            {
                _parentNameLength = parentIx;
                _itemNameLength = itemIx;
                _uncPrefixLength = uncPrefixLength;
            }

            //Returns the parent folder's full path
            public string ParentPath { get { return new String(_ff.Buffer, _uncPrefixLength, _parentNameLength - _uncPrefixLength); } }
            //Returns the UNC path to the parent folder
            public string ParentPathUnc { get { return new String(_ff.Buffer, 0, _parentNameLength); } }
            //Gets the full path of the file or folder
            public string FullPath { get { return new String(_ff.Buffer, _uncPrefixLength, _itemNameLength - _uncPrefixLength); } }
            //Returns the UNC path to the file or folder
            public string FullPathUnc { get { return new String(_ff.Buffer, 0, _itemNameLength); } }
            //Returns the file or folder name (with extension)
            public string Name { get { return new String(_ff.Buffer, _parentNameLength, _itemNameLength - _parentNameLength); } }
            //Returns the extenion or String.Empty
            public string Extension
            {
                get
                {
                    for (int ix = _itemNameLength; ix > _parentNameLength; --ix)
                        if (_ff.Buffer[ix] == '.')
                            return new String(_ff.Buffer, ix, _itemNameLength - ix);
                    return string.Empty;
                }
            }

            //Gets the length in bytes
            public long Length { get { return _ff.Value.nFileSizeLow | ((long)_ff.Value.nFileSizeHigh << 32); } }
            //Gets the file or folder attributes
            public FileAttributes Attributes { get { return _ff.Value.dwFileAttributes; } }
            //Gets the file or folder CreationTime in Utc
            public DateTime CreationTimeUtc { get { return _ff.Value.ftCreationTime.ToDateTimeUtc(); } }
            //Gets the file or folder LastAccessTime in Utc
            public DateTime LastAccessTimeUtc { get { return _ff.Value.ftLastAccessTime.ToDateTimeUtc(); } }
            //Gets the file or folder LastWriteTime in Utc
            public DateTime LastWriteTimeUtc { get { return _ff.Value.ftLastWriteTime.ToDateTimeUtc(); } }
            //Returns true if the file or folder is ReadOnly
            public bool IsReadOnly { get { return (Attributes & FileAttributes.ReadOnly) != 0; } }
            //Returns true if the file or folder is Hidden
            public bool IsHidden { get { return (Attributes & FileAttributes.Hidden) != 0; } }
            //Returns true if the file or folder is System
            public bool IsSystem { get { return (Attributes & FileAttributes.System) != 0; } }
            //Returns true if the file or folder is Directory
            public bool IsDirectory { get { return (Attributes & FileAttributes.Directory) != 0; } }
            //Returns true if the file or folder is ReparsePoint
            public bool IsReparsePoint { get { return (Attributes & FileAttributes.ReparsePoint) != 0; } }
            //Returns true if the file or folder is Compressed
            public bool IsCompressed { get { return (Attributes & FileAttributes.Compressed) != 0; } }
            //Returns true if the file or folder is Offline
            public bool IsOffline { get { return (Attributes & FileAttributes.Offline) != 0; } }
            //Returns true if the file or folder is Encrypted
            public bool IsEncrypted { get { return (Attributes & FileAttributes.Encrypted) != 0; } }
            // Captures the current state as a <see cref="FindFile.Info"/> structure.

            public Info GetInfo()
            {
                return new Info
                {
                    FullPath = FullPath,
                    Length = Length,
                    Attributes = Attributes,
                    CreationTimeUtc = CreationTimeUtc,
                    LastAccessTimeUtc = LastAccessTimeUtc,
                    LastWriteTimeUtc = LastWriteTimeUtc,
                };
            }
            //Gets or sets the Cancel flag to abort the current enumeration
            public bool CancelEnumeration
            {
                get { return _cancelEnumeration; }
                set { _cancelEnumeration = value; }
            }
        }

        private const string STAR = "*";
        private const char SLASH = '\\';
        private const char ZERO = '\0';
        //Returns the Unc path prefix used
        public const string UncPrefix = @"\\?\";

        private readonly Win32FindData _ff;

        private char[] _fpattern;
        private int _baseOffset;
        private bool _recursive;
        private bool _includeFolders;
        private bool _includeFiles;
        private bool _isUncPath;
        //Creates a FindFile instance.
        public FindFile() : this(UncPrefix, STAR, true, true, true) { }
        //Creates a FindFile instance.
        public FindFile(string rootDirectory) : this(rootDirectory, STAR, true, true, true) { }
        //Creates a FindFile instance.
        public FindFile(string rootDirectory, string filePattern) : this(rootDirectory, filePattern, true, true, true) { }
        //Creates a FindFile instance.
        public FindFile(string rootDirectory, string filePattern, bool recursive) : this(rootDirectory, filePattern, recursive, true, true) { }
        //Creates a FindFile instance.
        public FindFile(string rootDirectory, string filePattern, bool recursive, bool includeFolders) : this(rootDirectory, filePattern, recursive, includeFolders, true) { }
        //Creates a FindFile instance.
        public FindFile(string rootDirectory, string filePattern, bool recursive, bool includeFolders, bool includeFiles)
        {
            if (String.IsNullOrEmpty(rootDirectory) || String.IsNullOrEmpty(filePattern))
                throw new ArgumentException();

            _ff = new Win32FindData();
            _ff.BufferAddress = IntPtr.Zero;
            _ff.Buffer = new char[0x1000];
            _ff.Value = new Kernel32.WIN32_FIND_DATA();

            _recursive = recursive;
            _includeFolders = includeFolders;
            _includeFiles = includeFiles;

            BaseDirectory = rootDirectory;
            FilePattern = filePattern;
        }
        // The event-handler to raise when a file or folder is found
        public event EventHandler<FileFoundEventArgs> FileFound;
        // Gets or sets the maximum number of allowed characters in a complete path, default = 4kb

        public int MaxPath
        {
            get { return _ff.Buffer.Length; }
            set { Array.Resize(ref _ff.Buffer, Check.InRange(value, Kernel32.MAX_PATH, 0x100000)); }
        }
        private int UncPrefixLength { get { return _isUncPath ? 4 : 0; } }
        //Gets or sets the base directory to search within
        public string BaseDirectory
        {
            get { return new String(_ff.Buffer, UncPrefixLength, _baseOffset - UncPrefixLength); }
            set
            {
                if (value.IndexOfAny(InvalidFilePathChars) > 0)
                    throw new InvalidOperationException("Invalid characters in path.");

                if (!value.StartsWith(@"\\"))
                    value = UncPrefix + value;
                if (!value.EndsWith(@"\"))
                    value += @"\";

                _isUncPath = value.StartsWith(UncPrefix);
                value.CopyTo(0, _ff.Buffer, 0, _baseOffset = value.Length);
            }
        }
        // Gets or sets the file pattern to match while enumerating files and folders.

        public string FilePattern
        {
            get { return new String(_fpattern); }
            set
            {
                if (value.IndexOfAny(InvalidFilePatternChars) >= 0)
                    throw new InvalidOperationException("Invalid characters in pattern");
                _fpattern = value.TrimStart(SLASH).ToCharArray();
            }
        }
        //Gets or sets the Recursive flag
        public bool Recursive { get { return _recursive; } set { _recursive = value; } }
        //Gets or sets the IncludeFiles flag
        public bool IncludeFiles { get { return _includeFiles; } set { _includeFiles = value; } }
        //Gets or sets the IncludeFolders flag
        public bool IncludeFolders { get { return _includeFolders; } set { _includeFolders = value; } }
        //Gets or sets the RaiseOnAccessDenied flag, when set to true an 'Access Denied' can be raised
        public bool RaiseOnAccessDenied { get; set; }
        //Performs the search raising the FileFound event for each entry matching the request
        public void Find(string pattern)
        {
            FilePattern = pattern;
            Find();
        }
        //Performs the search raising the FileFound event for each entry matching the request
        public void Find()
        {
            Check.NotNull(FileFound);
            GCHandle hdl = GCHandle.Alloc(_ff.Buffer, GCHandleType.Pinned);
            try
            {
                FileFoundEventArgs args = new FileFoundEventArgs(_ff);
                _ff.BufferAddress = hdl.AddrOfPinnedObject();

                FindFileEx(args, _baseOffset);
            }
            finally
            {
                _ff.BufferAddress = IntPtr.Zero;
                hdl.Free();
            }
        }

        private bool IsWild()
        {
            return (_fpattern.Length == 1 && _fpattern[0] == '*')
                   ||
                   (_fpattern.Length == 3 && _fpattern[0] == '*' && _fpattern[1] == '.' && _fpattern[2] == '*');
        }

        private void FindFileEx(FileFoundEventArgs args, int slength)
        {
            Kernel32.FINDEX_INFO_LEVELS findInfoLevel = Kernel32.FINDEX_INFO_LEVELS.FindExInfoStandard;
            Kernel32.FINDEX_ADDITIONAL_FLAGS additionalFlags = 0;

            if (Environment.OSVersion.Version.Major >= 6)
            {
                //Ignore short-names
                findInfoLevel = Kernel32.FINDEX_INFO_LEVELS.FindExInfoBasic;
                //Use large fetch table
                additionalFlags = Kernel32.FINDEX_ADDITIONAL_FLAGS.FindFirstExLargeFetch;
            }

            _fpattern.CopyTo(_ff.Buffer, slength);
            _ff.Buffer[slength + _fpattern.Length] = ZERO;

            IntPtr hFile = Kernel32.FindFirstFileEx(
                _ff.BufferAddress,
                findInfoLevel,
                out _ff.Value,
                Kernel32.FINDEX_SEARCH_OPS.FindExSearchNameMatch,
                IntPtr.Zero,
                additionalFlags);

            if ((IntPtr.Size == 4 && hFile.ToInt32() == -1) ||
                (IntPtr.Size == 8 && hFile.ToInt64() == -1L))
            {
                Win32Error(Marshal.GetLastWin32Error());
                return;
            }

            bool traverseDirs = _recursive && IsWild();

            try
            {
                do
                {
                    int sposition = slength;
                    for (int ix = 0; ix < Kernel32.MAX_PATH && sposition < _ff.Buffer.Length && _ff.Value.cFileName[ix] != 0; ++ix)
                        _ff.Buffer[sposition++] = _ff.Value.cFileName[ix];

                    if (sposition == _ff.Buffer.Length)
                        throw new PathTooLongException();

                    if (!_ff.Value.IgnoredByName)
                    {
                        bool isDirectory = (_ff.Value.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory;

                        if ((_includeFolders && isDirectory) || (_includeFiles && !isDirectory))
                        {
                            args.SetNameOffsets(UncPrefixLength, slength, sposition);
                            FileFound(this, args);
                        }
                        if (traverseDirs && isDirectory)
                        {
                            _ff.Buffer[sposition++] = SLASH;
                            FindFileEx(args, sposition);
                        }
                    }
                } while (!args.CancelEnumeration && Kernel32.FindNextFile(hFile, out _ff.Value));
            }
            finally
            {
                Kernel32.FindClose(hFile);
            }

            // Recursive search for patterns other than '*' and '*.*' requires we enum directories again
            if (_recursive && !traverseDirs)
            {
                _ff.Buffer[slength] = '*';
                _ff.Buffer[slength + 1] = ZERO;

                hFile = Kernel32.FindFirstFileEx(
                    _ff.BufferAddress,
                    findInfoLevel,
                    out _ff.Value,
                    Kernel32.FINDEX_SEARCH_OPS.FindExSearchNameMatch,
                    IntPtr.Zero,
                    additionalFlags);

                if ((IntPtr.Size == 4 && hFile.ToInt32() == -1) || (IntPtr.Size == 8 && hFile.ToInt64() == -1L))
                {
                    Win32Error(Marshal.GetLastWin32Error());
                    return;
                }

                try
                {
                    do
                    {
                        int sposition = slength;
                        for (int ix = 0; ix < Kernel32.MAX_PATH && sposition < _ff.Buffer.Length && _ff.Value.cFileName[ix] != 0; ++ix)
                            _ff.Buffer[sposition++] = _ff.Value.cFileName[ix];

                        if (sposition == _ff.Buffer.Length)
                            throw new PathTooLongException();

                        if (!_ff.Value.IgnoredByName)
                        {
                            bool isDirectory = (_ff.Value.dwFileAttributes & FileAttributes.Directory) == FileAttributes.Directory;
                            if (isDirectory)
                            {
                                _ff.Buffer[sposition++] = SLASH;
                                FindFileEx(args, sposition);
                            }
                        }
                    } while (!args.CancelEnumeration && Kernel32.FindNextFile(hFile, out _ff.Value));
                }
                finally
                {
                    Kernel32.FindClose(hFile);
                }
            }
        }

        private void Win32Error(int errorCode)
        {
            switch (errorCode)
            {
                case Kernel32.ERROR_FILE_NOT_FOUND:
                case Kernel32.ERROR_PATH_NOT_FOUND:
                    return;
                case Kernel32.ERROR_ACCESS_DENIED:
                    if (!RaiseOnAccessDenied) return;
                    goto default;
                default:
                    throw new Win32Exception(errorCode);
            }
        }
    }

Check Class:

    using System;
    using System.Reflection;

    [System.Diagnostics.DebuggerNonUserCode]
    static partial class Check
    {
        public static void Assert<TException>(bool condition) where TException : Exception, new()
        {
            if (!condition)
                throw new TException();
        }

        public static void Assert<TException>(bool condition, string message) where TException : Exception, new()
        {
            if (!condition)
            {
                ConstructorInfo ci = typeof(TException).GetConstructor(new Type[] { typeof(string) });
                if (ci != null)
                {
                    TException e = (TException)ci.Invoke(new object[] { message });
                    throw e;
                }
                throw new TException();
            }
        }

        public delegate Exception ExceptionBuilder();

        public static void Assert(bool condition, ExceptionBuilder fnExceptionBuilder)
        {
            if (!condition)
                throw fnExceptionBuilder();
        }

        public static void Assert<TException>(bool condition, string message, Exception innerException) where TException : Exception, new()
        {
            if (!condition)
            {
                ConstructorInfo ci = typeof(TException).GetConstructor(new Type[] { typeof(string), typeof(Exception) });
                if (ci != null)
                {
                    TException e = (TException)ci.Invoke(new object[] { message, innerException });
                    throw e;
                }
                throw new TException();
            }
        }

        public static T NotNull<T>(T value)
        {
            if (value == null)
                throw new ArgumentNullException();
            return value;
        }

        public static string NotEmpty(string value)
        {
            if (value == null) throw new ArgumentNullException();
            if (value.Length == 0) throw new ArgumentOutOfRangeException();
            return value;
        }

        public static Guid NotEmpty(Guid value)
        {
            if (value == Guid.Empty)
                throw new ArgumentOutOfRangeException();
            return value;
        }

        public static T NotEmpty<T>(T value) where T : System.Collections.IEnumerable
        {
            if (value == null) throw new ArgumentNullException();
            if (!value.GetEnumerator().MoveNext()) throw new ArgumentOutOfRangeException();
            return value;
        }

        public static void IsEqual<T>(T a, T b) where T : IEquatable<T>
        {
            if (false == a.Equals(b))
                throw new ArgumentException();
        }

        public static void NotEqual<T>(T a, T b) where T : IEquatable<T>
        {
            if (true == a.Equals(b))
                throw new ArgumentException();
        }

        public static T[] ArraySize<T>(T[] value, int min, int max)
        {
            if (value == null)
                throw new ArgumentNullException();
            if (value.Length < min || value.Length > max)
                throw new ArgumentOutOfRangeException();
            return value;
        }

        public static T InRange<T>(T value, T min, T max) where T : IComparable<T>
        {
            if (value == null)
                throw new ArgumentNullException();
            if (value.CompareTo(min) < 0)
                throw new ArgumentOutOfRangeException();
            if (value.CompareTo(max) > 0)
                throw new ArgumentOutOfRangeException();
            return value;
        }

        public static T IsAssignable<T>(object value)
        {
            return (T)IsAssignable(typeof(T), value);
        }

        public static object IsAssignable(Type toType, object fromValue)
        {
            NotNull(toType);
            if (fromValue == null)
            {
                if (toType.IsValueType)
                    throw new ArgumentException(string.Format("Can not set value of type {0} to null.", toType));
            }
            else
                IsAssignable(toType, fromValue.GetType());
            return fromValue;
        }

        public static void IsAssignable(Type toType, Type fromType)
        {
            if (!NotNull(toType).IsAssignableFrom(NotNull(fromType)))
                throw new ArgumentException(string.Format("Can not set value of type {0} to a value of type {1}", toType, fromType));
        }
    }


Что я уже пробовал:

..........
..........
..........

Patrice T

Можете ли вы объяснить, что делает этот код ?

Philippe Mori

Слишком много кода... для вопроса на форуме.

Philippe Mori

Если у вас есть проблемы с производительностью, то вы используете профилировщик!

1 Ответов

Рейтинг:
4

Dave Kreskowiak

Вы ничего не используете в System.IO-ну, кроме пути.GetInvalidPathCharacters. Вы в значительной степени напрасно переписали хороший кусок System.IO.

На самом деле вы мало что можете сделать, чтобы сделать этот код быстрее. Проблема в том, что вы просматриваете 700 000 файлов и папок.

Вы либо должны проиндексировать все это заранее и просто искать в своем индексе то, что вам нужно, либо...

Другой вариант-использовать поиск Windows. Убедитесь, что для корневой папки, содержащей все эти файлы и папки, включена функция "разрешить индексирование содержимого файлов в этой папке...". Затем вы можете запросить поиск Windows, чтобы вернуть то, что вы ищете, гораздо быстрее, чем делать это с помощью методов файлов и каталогов.

Поиск Windows (Windows)[^]
Google " C# Windows Search"[^]