4

I need my installer setup to check the first line of a txt file during setup, and compare it with any number I want.

This is the txt file:

enter image description here

This is the code I am trying to edit:

function GetKeyValue(const AKeyName, AFileName, ADefault: string): string;
var  
  I: Integer;
  KeyPos: Integer;
  KeyFull: string;
  FileLines: TArrayOfString;
begin
  Result := ADefault;
  if LoadStringsFromFile(AFileName, FileLines) then
  begin
    KeyFull := AKeyName;
    for I := 0 to GetArrayLength(FileLines) - 1 do
    begin
      FileLines[I] := TrimLeft(FileLines[I]);
      KeyPos := Pos(KeyFull, FileLines[I]);
      if KeyPos > 0 then
      begin
        Result := Copy(FileLines[I], KeyPos + Length(AKeyName) + 1, MaxInt);
        Break;
      end;
    end;
  end;
end;

var
  // target version label must be declared globally
  L2Ver2: TLabel;

procedure DirEditChange(Sender: TObject);
var
  FilePath: string;
begin
  // assign the expected INF file path
  FilePath := AddBackslash(WizardForm.DirEdit.Text) + 'Sam.inf';
  // I WANT TO READ THE FIRST LINE OF THE TXT FILE AND return N/A if not found
  L2Ver2.Caption := GetKeyValue('', FilePath, 'N/A');
end;

procedure InitializeWizard;
begin
  // create the target label as before
  L2Ver2 := TLabel.Create(WizardForm);
  ...
  // bind the DirEditChange method to the directory edit's OnChange event
  WizardForm.DirEdit.OnChange := @DirEditChange;  
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  // if the page has been turned to the select directory page, update the
  // label caption by firing the assigned OnChange event method manually
  if (CurPageID = wpSelectDir) then
    DirEditChange(nil);
end;

I got the code from this post: Inno Setup - How to read an INF file during the Setup

I'am not sure about how to edit the function GetKeyValue and this other part of the code L2Ver2.Caption := GetKeyValue('', FilePath, 'N/A');

Community
  • 1
  • 1
Dielo
  • 603
  • 2
  • 12
  • 25

1 Answers1

6

How to read a specific line from a text file ?

Except the LoadStringsFromFile function there's not much you can use from that function. The next function I wrote loads a text file given by the FileName parameter, and tries to copy the line from the 0 based index Index to the output parameter Line. If loading of the given file succeed and the file has enough lines to satisfy the requested Index, it returns True, False otherwise.

function TryGetFileLine(const FileName: string; Index: Integer; out Line: string): Boolean;
var  
  FileLines: TArrayOfString;
begin
  // the function succeed when the file can be loaded and the count of lines is
  // greater than the requested line index (it is 0 based index, hence the line
  // count must be greater)
  Result := LoadStringsFromFile(FileName, FileLines) and (GetArrayLength(FileLines) > Index);
  // if the above succeeded, return the file line of the requested index to the
  // output parameter
  if Result then
    Line := FileLines[Index];
end;

For shorter code I chose 0 based indexing, so if you would like to read the first line of the file, you would request the index 0, second line index 1 and so forth. To get the first line it would be:

var
  S: string;
begin
  // if the file could be opened and has at least one line, then the following
  // call succeed and the variable S will contain the first line of the file
  if TryGetFileLine('C:\File.txt', 0, S) then
    MsgBox('The first line of the given file is: ' + S, mbInformation, MB_OK);
end;

How to convert string to integer ?

There are at least two ways to convert string to 32-bit integer in Inno Setup at this time. The StrToInt function and StrToIntDef. The first one tries to convert the passed string to integer and if that fails, it returns -1. The second does the same except if the conversion fails, it returns the value specified by the def parameter.

Unfortunately, none of the above functions can reliably tell if the given string has been converted without losing one value in the integer range. Consider the following code:

var
  Value1: Integer;
  Value2: Integer;
begin
  Value1 := StrToInt('-1');
  Value2 := StrToInt('Non integer');

  if Value1 = Value2 then
  begin
    MsgBox('Err, the result is the same for string of value -1 and for a non ' +
      'integer string.', mbInformation, MB_OK);
  end;
end;

The above code demostrates, that if you'll use the StrToInt function, you won't be able to determine whether the string (in your case a line read from file) contains the value -1 or a non integer value. The similar applies to the def parameter of the StrToIntDef function.

However, you can sanitize this problem if you explicitly check if the string contains value that the function returns in case when the conversion fails. The following function returns True if the string S contains a valid integer value, False otherwise. If the conversion succeeds, the converted value is returned to the output Value parameter:

function TryStrToInt(const S: string; out Value: Integer): Boolean;
var
  I: Integer;
begin
  I := StrToIntDef(S, -1);
  Result := (I <> -1) or (S = '-1');
  if Result then
    Value := I;
end;

Usage of this function looks like this:

var
  I: Integer;
begin
  if TryStrToInt('12345', I) then
  begin
    MsgBox('The passed string was converted to integer. Its value is: ' +
      IntToStr(I), mbInformation, MB_OK);
  end;
end;

Safely to the end...

You didn't mention what are the version values in your text file, so I've assumed they are 32-bit integers and might have any value in the range (even though I believe in real you'll be using only positive values where the built-in string to integer conversion functions might suffice).

Still, having a safer string to integer conversion function in the codebase is fine. So let's stick the above functions together to attempt to read the first line of a text file and convert it to integer:

var
  S: string;
  I: Integer;
begin
  // if the first line of the file was successfully read and could have been
  // converted to integer, then...
  if TryGetFileLine('C:\File.txt', 0, S) and TryStrToInt(S, I) then
  begin
    MsgBox('The first line of the given file was successfully read and could ' +
      'have been converted to integer. Its value is: ' + IntToStr(I),
      mbInformation, MB_OK);
    // here the variable I contains the value that you can compare in a way
    // of your choice
  end;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
  • Hello Tlama, what i am trying to do is something like what i asked in this other post: [link](http://stackoverflow.com/questions/15226096/inno-setup-how-to-read-an-inf-file-during-the-setup) but instead of a malformed inf file, i want to use a txt file. – Dielo Sep 03 '14 at 15:37
  • I want to check/compare the version of the program am installing by reading the first line of one txt file. `var L2Ver2: TLabel; procedure DirEditChange(Sender: TObject); var FilePath: string; begin FilePath := AddBackslash(WizardForm.DirEdit.Text) + 'Sam.inf'; // IN HERE I WANT THE SCRIPT TO READ THE FIRST LINE OF THE TXT AND SHOW IT IN THE LABEL, return N/A if not found L2Ver2.Caption := GetKeyValue('PatchVersion', FilePath, 'N/A'); end;` – Dielo Sep 03 '14 at 15:38
  • I got the script to compare and i made it work in the last script to edit the inf file... my problem right now is in this part of the code: `var FilePath: string; S: string; begin FilePath := AddBackslash(WizardForm.DirEdit.Text) + 'Xfolder\version.txt; L2Ver2.Caption := TryGetFileLine(FilePath,0, S);` – Dielo Sep 03 '14 at 15:57
  • Ok, but what about your *"compare it with any number I want"* requirement ? For comparing numeric value read from a text file is the best to convert it to integer (that's why I wrote the part about string to integer conversion). But if your aim is only to display the first line of a certain text file in that label if such file exists and has at least one line (otherwise `N/A`), you can use a [`code like this`](http://pastebin.com/NSWTfnhg). – TLama Sep 03 '14 at 15:57
  • My friend right now what i need is help to get the value of that txt file in the label text... after i get that value i can compare it with any number i want... i know how to do that... what i do not know is how to read that number and put it in a label text... like the other post [link](http://stackoverflow.com/questions/15226096/inno-setup-how-to-read-an-inf-file-during-the-setup) – Dielo Sep 03 '14 at 16:02
  • I see. The `TryGetFileLine` function returns boolean (state indicating whether the reading of the requested file line succeeded or not), so you cannot assign it to a string property. The line read from the file by that function is in the `S` variable. So you could write something like `if TryGetFileLine(FilePath, 0, S) then L2Ver2.Caption := S else L2Ver2.Caption := 'N/A'`, or use a wrapper function like I shown in the linked pastebin from my previous comment. – TLama Sep 03 '14 at 16:05
  • Can we start a discussion chat for me to post the full code to get a better answer in here ? – Dielo Sep 03 '14 at 16:06
  • Ok, http://chat.stackoverflow.com/rooms/60553/http-stackoverflow-com-q-25598144-960757 – TLama Sep 03 '14 at 16:08