Search code examples
c#directorygetfiles

Is there a way to stop a Directory.GetFiles search


I'm using Directory.GetFiles() to search files in a multi-folder directory and some of these folders are too big so the search could take up to 60 seconds. In the code below a search starts a second after the user stops typing, this works great when the user types the file name and does not pauses while typing it but if he/she pauses in the middle of the word the search will start and will not stop until it finishes even if he/she starts typing again.

What I would like to do is stop searches while the user is typing, possibly in the inputFileName_TextChanged method.

Is there a way to stop a search that has been already started when using Directory.GetFiles?

using System.Timers;
using System.IO;

namespace findFileTest
{

    public partial class MainWindow : Window
    {
        Timer timer = new Timer();

        public MainWindow()
        {
            InitializeComponent();

            timer.Elapsed += new ElapsedEventHandler(TimerEvent);
            timer.Interval = 1000;
            timer.Enabled = true;
            timer.Stop();
        }

        private void inputFileName_TextChanged(object sender, TextChangedEventArgs e)
        {
            // How to stop the search currently in process here
            timer.Stop();
            timer.Start();
        }

        public void TimerEvent(object source, ElapsedEventArgs e)
        {
            timer.Stop();
            findFile();
        }

        private void findFile()
        {
            string fName = "";

            this.Dispatcher.Invoke(() => {
                fName = inputFileName.Text;
            });

            var fType = ".txt";
            var fileName = fName.Trim() + fType;

            var file = Directory.GetFiles(@"C:\TestFolder\", fileName, SearchOption.AllDirectories).FirstOrDefault();
            if (file == null) {
                Console.WriteLine("File not found");
            }
            else {
                Console.WriteLine("Found:" + file);
            }
        }
    }
}

Solution

  • Is there a way to stop a search that has been already started when using Directory.GetFiles?

    No, not safely. The GetFiles() method doesn't provide any mechanism to interrupt its work before it returns. You would have to abort the thread, which would cause a host of other problems.

    As noted in this comment, taking your request literally the answer would be to use the Directory.EnumerateFiles() method instead. This method surfaces the semantics of the underlying FindFirstFile() and FindNextFile(), by providing an enumerator that returns one file at a time. Every time a new file is returned, you have the opportunity to interrupt the enumeration and return without doing more work.

    Note that even using EnumerateFiles(), you won't get the first file result until it finds something that matches your search pattern. It is likely that if you switch to using EnumerateFiles(), you'll also want to do your own matching on the input, rather than letting EnumerateFiles() do it. I.e. pass "*" as the search string. This will slow the overall operation down a little, but you'll get a chance to interrupt the operation for every file in the search directory and subdirectories, instead of only when a matching file is found.

    Finally, I'll suggest that given your apparent use case, you may want to rethink using the Directory class at all. Based on the code you posted, you are initiating a search of the file system, a necessarily slow operation, every time the user has entered some new text. IMHO, it would be better to build an index once (e.g. a Dictionary<string, List<string>> that maps the file name without extension — i.e. Path.GetFileNameWithoutExtension() — to the full paths of each matching file in the directory). In that way, you don't have to interrupt anything. The "search" part after the user's entered text will be nearly instantaneous.

    There will of course be an initial delay before the directory is fully indexed. It's up to you how you'd want to deal with that. But I'd recommend providing a mechanism both to indicate to the user that the indexing isn't fully complete, but also doing the check on what you have, just in case the file the user's looking for matches a file that was already indexed.

    Note that the usual Windows file systems that are used — FAT, FAT32, NTFS, etc. — are case-insensitive, so you'd want to initialize your dictionary with StringComparer.OrdinalIgnoreCase. "Ordinal", because the file system names need to match the exact characters a user types, and "ignore case" so that the search is case-insensitive.

    I'd guess that in your scenario, it's unlikely files would be added or removed while the user is using your program to search for them. But if you are concerned about that scenario, you can either just provide the user a "Refresh" button to force the directory to be re-indexed, or use FileSystemWatcher to update the index automatically if files are added or removed.