在 Inno Setup 中找到具有特定名称模式和内容的 JSON 文件

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

我有一个工作脚本,安装程序在其中找到特定路径中的特定文件,但我需要稍微更改它。我注意到当我重新安装应用程序时,我想从中获取信息的文件的名称每次都不同,所以它并没有我想的那么重要。

虽然有些东西没有改变 - 变量文件名的路径,我已经使用常量定义并且可以再次使用,还有文件扩展名。因此,如果路径没有改变,那么搜索过程可能会快得多。文件格式为 JSON,脚本中已应用其代码。

这是 JSON 结构示例:

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

唯一的解决办法是搜索特定路径中具有特定扩展名的所有文件。我需要在这里使用一些变量,第一个已经在代码中提到 InstallLocation 这是正确的安装路径,我需要定义的下一个变量是 DisplayName,它包含应用程序名称,可能还有另一个定义文件扩展名的名称。

所以我需要找到正确的文件,包含DisplayName参数中的特定字符串,比较是否与定义的相同,然后从InstallLocation参数中读取安装路径。

这是我目前的代码:

[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 'MultiByteToWideChar@kernel32.dll 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;

感谢您的帮助


我已经编写了我的代码原型来展示我如何看待代码来实现我想要的(最直观/最清晰的方式):

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;

要查找具有特定扩展名的文件,请使用 FindFirst and FindNext 函数。

对于您找到的每个匹配文件,使用您已有的代码检查其内容。

您需要进一步调整代码,因为我不清楚在出现各种错误时您要做什么。

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;