peterx684
peterx684

Reputation: 23

Find JSON file with specific name pattern and contents in Inno Setup

I have a working script, where the installer finds the specific file inside specific path but I need to slightly change it. I noticed that when I reinstall the application, the name of the file which I want to get the info from is different every time, so it's not that important as I thought.

There are some things which are not changing although - the path to that variable filename, which I have defined already using the constant and can be used again and also the file extension. So if the path doesn't change, the search process could be a lot more quicker just for that. The file format is JSON and code for it is already applied in the script.

Here is the JSON structure sample:

"ChunkDbs": [],
"CompatibleApps": [],
"DisplayName": "Application Name",
"InstallLocation": "D:\\Program Files (x86)\\ApplicationName",
"InstallTags": [],
"InstallComponents": [],

The only solution for this is searching all the files in specific path with specific extension. There are some variables I need to use here, the first is already mentioned in the code as InstallLocation which is the proper install path, the next variable which I need to define is DisplayName, which contains the application name and probably another one that define the file extension.

So I need to find the right file, containing the specific string inside DisplayName parameter, compare if it's the same as in the defined one and then read the installation path from InstallLocation parameter.

Here is the code I have so far:

[Setup]
DefaultDirName={code:GetInstallLocation}

[Code]
#include "JsonParser.pas"

function ParseJsonAndLogErrors(
  var JsonParser: TJsonParser; const Source: WideString): Boolean;
var
  I: Integer;
begin
  ParseJson(JsonParser, Source);

  Result := (Length(JsonParser.Output.Errors) = 0);
  if not Result then
  begin
    Log('Error parsing JSON');
    for I := 0 to Length(JsonParser.Output.Errors) - 1 do
    begin
      Log(JsonParser.Output.Errors[I]);
    end;
  end;
end;

function GetJsonRoot(Output: TJsonParserOutput): TJsonObject;
begin
  Result := Output.Objects[0];
end;

function FindJsonValue(
  Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
  var Value: TJsonValue): Boolean;
var
  I: Integer;
begin
  for I := 0 to Length(Parent) - 1 do
  begin
    if Parent[I].Key = Key then
    begin
      Value := Parent[I].Value;
      Result := True;
      Exit;
    end;
  end;

  Result := False;
end;

function FindJsonString(
  Output: TJsonParserOutput; Parent: TJsonObject; Key: TJsonString;
  var Str: TJsonString): Boolean;
var
  JsonValue: TJsonValue;
begin
  Result :=
    FindJsonValue(Output, Parent, Key, JsonValue) and
    (JsonValue.Kind = JVKString);
  if Result then
  begin
    Str := Output.Strings[JsonValue.Index];
  end;
end;

function MultiByteToWideChar(
  CodePage: UINT; dwFlags: DWORD; const lpMultiByteStr: AnsiString;
    cchMultiByte: Integer; lpWideCharStr: string; cchWideChar: Integer): Integer;
  external '[email protected] stdcall';  

function LoadStringFromFileInCP(
    FileName: string; var S: string; CP: Integer): Boolean;
var
  Ansi: AnsiString;
  Len: Integer;
begin
  Result := LoadStringFromFile(FileName, Ansi);
  if Result then
  begin
    Len := MultiByteToWideChar(CP, 0, Ansi, Length(Ansi), S, 0);
    SetLength(S, Len);
    MultiByteToWideChar(CP, 0, Ansi, Length(Ansi), S, Len);
  end;
end;

const
  CP_UTF8 = 65001;

var
  InstallLocation: string;

<event('InitializeSetup')>
function InitializeSetupParseConfig(): Boolean;
var
  Json: string;
  ConfigPath: string;
  JsonParser: TJsonParser;
  JsonRoot: TJsonObject;
  S: TJsonString;
begin
  Result := True;
  ConfigPath := ExpandConstant('{commonappdata}\Data\FEE8D728379C5E.dat');
  Log(Format('Reading "%s"', [ConfigPath]));
  if not LoadStringFromFileInCP(ConfigPath, Json, CP_UTF8) then
  begin
    MsgBox(Format('Error reading "%s"', [ConfigPath]), mbError, MB_OK);
    Result := True;
  end
    else
  if not ParseJsonAndLogErrors(JsonParser, Json) then
  begin
    MsgBox(Format('Error parsing "%s"', [ConfigPath]), mbError, MB_OK);
    Result := True;
  end
    else
  begin 
    JsonRoot := GetJsonRoot(JsonParser.Output);
    if not FindJsonString(JsonParser.Output, JsonRoot, 'InstallLocation', S) then
    begin
      MsgBox(Format('Cannot find InstallLocation in "%s"', [ConfigPath]),
        mbError, MB_OK);
      Result := False;
    end
      else
    begin
      InstallLocation := S;
      Log(Format('Found InstallLocation = "%s"', [InstallLocation]));
    end;
    ClearJsonParser(JsonParser);
  end;
end;

function GetInstallLocation(Param: string): string;
begin
  Result := InstallLocation;
end;

Thank you for help


I've written my code prototype to show how I see the code to make what I want (the most intuitive / clearest way):

const
  MyDisplayName = 'MyReqAppName';
  MyPath = 'C:\some\path\*.dat';

var
  RealDisplayName: string;
  InstallLocation: string;

function FindParameters();
begin
  RealDisplayName := 'DisplayName';
  InstallLocation := 'InstallLocation';
  Find(RealDisplayName in MyPath);
  if (RealDisplayName = MyDisplayName) then
    Find(InstallLocation);
  else
    repeat
      Find(RealDisplayName in MyPath);
    until(RealDisplayName = MyDisplayName);
end;

Upvotes: 1

Views: 571

Answers (1)

Martin Prikryl
Martin Prikryl

Reputation: 202118

To find a files with a specific extension, use FindFirst and FindNext functions.

For each matching file you find, inspect its contents using the code you already have.

You will need to adapt the code further, as it's not clear to me what you want to do in case of various kinds of errors.

var
  Path: string;
  FindRec: TFindRec;
  Json: string;
  ConfigPath: string;
  JsonParser: TJsonParser;
  JsonRoot: TJsonObject;
  S: TJsonString;
  DisplayName: string;
begin
  Path := 'C:\some\path';
  if FindFirst(Path + '\*.dat', FindRec) then
  begin
    repeat
      Log('Found: ' + FindRec.Name);
      ConfigPath := Path + '\' + FindRec.Name;
      Log(Format('Reading "%s"', [ConfigPath]));
      if not LoadStringFromFileInCP(ConfigPath, Json, CP_UTF8) then
      begin
        Log(Format('Error reading "%s"', [ConfigPath]));
      end
        else
      if not ParseJsonAndLogErrors(JsonParser, Json) then
      begin
        Log(Format('Error parsing "%s"', [ConfigPath]));
      end
        else
      begin 
        JsonRoot := GetJsonRoot(JsonParser.Output);
        if not FindJsonString(JsonParser.Output, JsonRoot, 'DisplayName', S) then
        begin
          Log(Format('Cannot find DisplayName in "%s"', [ConfigPath]));
        end
          else
        if DisplayName <> MyDisplayName then
        begin
          Log(Format('DisplayName is "%s", not what we want', [DisplayName]));
        end
          else
        begin
          Log(Format('DisplayName is "%s", what we want', [DisplayName]));
          if not FindJsonString(
                   JsonParser.Output, JsonRoot, 'InstallLocation', S) then
          begin
            Log(Format('Cannot find InstallLocation in "%s"', [ConfigPath]));
          end
            else
          begin
            InstallLocation := S;
            Log(Format('Found InstallLocation = "%s"', [InstallLocation]));
          end;
        end;
        ClearJsonParser(JsonParser);
      end;
    until not FindNext(FindRec);
    FindClose(FindRec);
  end;
end;

Upvotes: 1

Related Questions