recursioninstallationinno-setuppascalscriptfile-search

Inno Setup: How do I recursively list all directories in a given path that includes a specific file (in this case, '240.json')?


I am currently stuck with this bit of code that only gives me the very first occurrence of a specific file in the top most folder of a given directory that I am trying to find. I want to modify this to list/return all occurrences of the file.

{ Credits to (I believe) TLama for some/most of this excerpt of code. }

{ Looks for specific file }
var
  filelocation: string;
  { ^ Global Variable }

function FindFile(RootPath: string; FileName: string): string;
var
  FindRec: TFindRec;
  FilePath: string;
begin
  Log(Format('', [RootPath, FileName]));

  if FindFirst(RootPath + '\*', FindRec) then
  begin
    try
      repeat
        if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
        begin
          FilePath := RootPath + '\' + FindRec.Name;
          if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
          begin
            Log(FilePath);
            Result := FindFile(FilePath, FileName);
            if Result <> '' then Exit;
          end
            else
          if CompareText(FindRec.Name, FileName) = 0 then
          begin 
            { list each \userdata\(numbers here) here that include 240.json within them }
            Log(Format('User' + {user} + ' owns (Game Here) on Steam.', [FilePath]));

            Result := FilePath;
            filelocation := FilePath
          end;                             
        end;
      until not FindNext(FindRec);
    finally
      FindClose(FindRec);
    end;
  end
    else
  begin
    Log(Format('Failed to list %s', [RootPath]));
    MsgBox('You do not own (Game Here). Please purchase and install it from Steam.',
           mbError, MB_OK);
    Exit;
  end;
end;

Solution

  • The following code collects all found files into TStringList:

    procedure FindFile(RootPath: string; FileName: string; FileLocations: TStringList);
    var
      FindRec: TFindRec;
      FilePath: string;
    begin
      Log(Format('Looking for %s in %s', [FileName, RootPath]));
    
      if FindFirst(RootPath + '\*', FindRec) then
      begin
        try
          repeat
            if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
            begin
              FilePath := RootPath + '\' + FindRec.Name;
              if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
              begin
                FindFile(FilePath, FileName, FileLocations);
              end
                else
              if CompareText(FindRec.Name, FileName) = 0 then
              begin 
                Log(Format('Found %s', [FilePath]));
    
                { Here you can do additional check (for file contents) }
    
                FileLocations.Add(FilePath);
    
                Break;
              end;                             
            end;
          until not FindNext(FindRec);
        finally
          FindClose(FindRec);
        end;
      end
        else
      begin
        Log(Format('Failed to list %s', [RootPath]));
      end;
    end;
    

    You can use it like:

    var
      FileLocations: TStringList;
    begin
      FileLocations := TStringList.Create();
      FindFile('C:\some\path', '240.json', FileLocations);
    end;
    

    And then process the FileLocations as you need:

    for I := 0 to FileLocations.Count - 1 do
    begin
      FileName := FileLocations[I];
      { Process FileName }
    end;
    

    Though if you do not need to process the files multiple times, you can as well process them directly in FindFile and you do not need to collect the names into a list.