What's the fastest way to list all the exe files in a huge directory in Delphi?

Viewed 330

At the moment I'm doing something like this:

var
    Files: TArray<String>;

if IncludeSubDirs then
    Files := TDirectory.GetFiles(Path, '*.exe', TSearchOption.soAllDirectories)
else
    Files := TDirectory.GetFiles(Path, '*.exe', TSearchOption.soTopDirectoryOnly);

Path is a user defined String that can point to any existing directory. For "big" directories with a ton of files and with IncludeSubDirs = True (C:\Windows\ for example) GetFiles takes a very long time (like 30+ secs).

What would be the fastest way to list all the exe files in a "big" directory under Windows with Delphi (if any)?

1 Answers

I did some benchmarking and for a huge directory FindFirst / FindNext is about 1.5 to 3% faster than using TDirectory. I would say than both are equivalent in speed (for my use case I saved about 1 sec per minute). I ended up using FindFirst / FindNext since you get results progressively and not all at once, memory management seemed better and it's easier to cancel midway. I also used a TThread to avoid blocking my UI.

This is what I ended up with:

procedure TDirectoryWorkerThread.AddToTarget(const Item: String);
begin
    if (not Self.Parameters.DistinctResults) or (Self.Target.IndexOf(Item) = -1) then
        Self.Target.Add(Item);
end;

procedure TDirectoryWorkerThread.ListFilesDir(Directory: String);

var
    SearchResult: TSearchRec;

begin
    Directory := IncludeTrailingPathDelimiter(Directory);

    if FindFirst(Directory + '*', faAnyFile, SearchResult) = 0 then
    begin
        try
            repeat
                if (SearchResult.Attr and faDirectory) = 0 then
                begin
                    if (Self.Parameters.AllowedExtensions = nil) or (Self.Parameters.AllowedExtensions.IndexOf(ExtractFileExt(SearchResult.Name)) <> -1) then
                        AddToTarget(Directory + SearchResult.Name);
                end
                else if Self.Parameters.IncludeSubDirs and (SearchResult.Name <> '.') and (SearchResult.Name <> '..') then
                    ListFilesDir(Directory + SearchResult.Name);
            until Self.Terminated or (FindNext(SearchResult) <> 0);
        finally
            FindClose(SearchResult);
        end;
    end;
end;
Related