6

I'm reading the value of an INF file, now I need to compare it with the installer version, but when I compile I get an error:

Unknown identifier: CompareVersion

What is wrong?

[Code]

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
  L2Ver2: TLabel;

procedure DirEditChange(Sender: TObject);
var
  FilePath: string;
begin
  FilePath := AddBackslash(WizardForm.DirEdit.Text) + 'left4dead2\steam.inf';
  L2Ver2.Caption := GetKeyValue('PatchVersion', FilePath, 'N/A');
  if (CompareVersion( L2Ver2.Caption, '{#MyAppOldVersion}') = 0) then
    begin
      ...
    end 
end;

I have the error in this line:

if (CompareVersion( L2Ver2.Caption, '{#MyAppOldVersion}') = 0) then

{#MyAppOldVersion} is already defined.

This post is related to my previous question: Read an INF file during the Setup.

If anyone can help, I would appreciate.

Community
  • 1
  • 1

3 Answers3

16

There's no CompareVersion function in Inno Setup (there is one since 6.1, see below).

You need to define your own.

Something like:

function CompareVersion(V1, V2: string): Integer;
var
  P, N1, N2: Integer;
begin
  Result := 0;
  while (Result = 0) and ((V1 <> '') or (V2 <> '')) do
  begin
    P := Pos('.', V1);
    if P > 0 then
    begin
      N1 := StrToInt(Copy(V1, 1, P - 1));
      Delete(V1, 1, P);
    end
      else
    if V1 <> '' then
    begin
      N1 := StrToInt(V1);
      V1 := '';
    end
      else
    begin
      N1 := 0;
    end;
  
    P := Pos('.', V2);
    if P > 0 then
    begin
      N2 := StrToInt(Copy(V2, 1, P - 1));
      Delete(V2, 1, P);
    end
      else
    if V2 <> '' then
    begin
      N2 := StrToInt(V2);
      V2 := '';
    end
      else
    begin
      N2 := 0;
    end;

    if N1 < N2 then Result := -1
      else
    if N1 > N2 then Result := 1;
  end;
end;
  • Returns 0, if the versions are equal.
  • Returns -1, if the V1 is older than the V2.
  • Returns 1, if the V1 is newer than the V2.
  • 1.12 is considered newer than 1.1.
  • 1.1 is considered the same as 1.1.0.
  • Throws an exception, when a version is syntactically invalid (only digits and dots are allowed).

Inno Setup 6.1 and newer supports version comparing natively with its ComparePackedVersion function.

Use GetPackedVersion to obtain binary version in the required form. And PackVersionComponents or StrToVersion to calculate the packed version for a specific (hardcoded) version for the comparison.


Unit tests:

procedure CompareVersionTest(V1, V2: string; Expected: Integer);
var
  R: Integer;
  M: string;
begin
  M := Format('Comparing [%s] vs. [%s] - expecting %d - ', [V1, V2, Expected]);
  R := CompareVersion(V1, V2);
  if R <> Expected then
  begin
    RaiseException(M + Format('Failed - Got %d', [R]));
  end
    else
  begin
    Log(M + 'Passed');
  end;
end;

procedure CompareVersionTests;
begin
  CompareVersionTest('1', '1', 0);
  CompareVersionTest('1.2', '1.2', 0);
  CompareVersionTest('1.2.3', '1.2.3', 0);
  CompareVersionTest('1', '2', -1);
  CompareVersionTest('1', '3', -1);
  CompareVersionTest('2', '3', -1);
  CompareVersionTest('2', '1', 1);
  CompareVersionTest('3', '1', 1);
  CompareVersionTest('3', '2', 1);
  CompareVersionTest('1', '12', -1);
  CompareVersionTest('12', '2', 1);
  CompareVersionTest('12', '12', 0);
  CompareVersionTest('1.2', '1.3', -1);
  CompareVersionTest('1.3', '1.2', 1);
  CompareVersionTest('1.2', '11.2', -1);
  CompareVersionTest('11.2', '1.2', 1);
  CompareVersionTest('1.1', '1.12', -1);
  CompareVersionTest('1.11', '1.12', -1);
  CompareVersionTest('1.12', '1.1', 1);
  CompareVersionTest('1.12', '1.11', 1);
  CompareVersionTest('1', '1.0', 0);
  CompareVersionTest('1.0', '1', 0);
  CompareVersionTest('1', '1.1', -1);
  CompareVersionTest('1.1', '1', 1);
  CompareVersionTest('1.12.123', '1.12.124', -1);
  CompareVersionTest('1.12.124', '1.12.123', 1);
  CompareVersionTest('1.12.123', '1.13.1', -1);
  CompareVersionTest('1.13.1', '1.12.123', 1);
  CompareVersionTest('1.12.123', '1.13', -1);
  CompareVersionTest('1.13', '1.12.123', 1);
  CompareVersionTest('1.12', '1.13.1', -1);
  CompareVersionTest('1.13.1', '1.12', 1);
end;

If you want to run the unit tests, just call CompareVersionTests in InitializeSetup.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • you have helped me a lot, thank you! it works perfectly – Williams Mogollon Jun 15 '16 at 21:57
  • 2
    +1. I moved version number extracting to `function NextIntFromVersion(var S: string): Integer;` and then in `CompareVersion` in while loop I just have `Result := NextIntFromVersion(V1) - NextIntFromVersion(V2);`. It changes return values a little: for `V1` older than `V2` you just get negative number, for `V1` newer than `V2` - positive. Other than that it works the same and is a bit shorter. :) – Tithen-Firion Mar 23 '18 at 16:40
  • 1
    If anyone needs it, you can avoid exceptions and assume `0` if you replace each `StrToInt` with `StrToIntDef` (use `0` as the 2nd parameter). – Bill_Stewart Dec 10 '19 at 21:15
3

Using InnoSetup 6.1 or newer, you can use the built-in function ComparePackedVersion. It expects version numbers in binary format (Int64), so you first have to convert each version using StrToVersion from string to binary format.

The following function encapsulates all of this.

function CompareVersion( version1, version2: String ): Integer;
var
    packVersion1, packVersion2: Int64;
begin
    if not StrToVersion( version1, packVersion1 ) then packVersion1 := 0;
    if not StrToVersion( version2, packVersion2 ) then packVersion2 := 0;
    Result := ComparePackedVersion( packVersion1, packVersion2 );
end;
  • Returns 0, if the versions are equal.
  • Returns -1, if version1 is older than version2.
  • Returns 1, if version1 is newer than version2.
  • If a version string is in incorrect format (StrToVersion returns False) it is considered to be 0.0.0.0. No exception will be thrown.
  • The function passes the unit test from Martin Prikryl's answer.
zett42
  • 25,437
  • 3
  • 35
  • 72
2

I came up with this solution that is a bit shorter by using recursion. I wrote it as a procedure in order to return the result from the recursion correctly. Maybe my parameter usage isn't perfect, but it works.

procedure VerStrCompare(S1,S2:String; var pComp:Integer);
var nE1,nE2,nV1,nV2:Integer;

begin
 if (Length(S1)>0) OR (Length(S2)>0) then begin
  nE1:=Pos('.',S1+'.');
  nE2:=Pos('.',S2+'.');
  if nE1>1 then nV1:=StrToInt(Copy(s1,1,(nE1-1))) else nV1:=0;
  if nE2>1 then nV2:=StrToInt(Copy(s2,1,(nE2-1))) else nV2:=0;
  if nV1=nV2 then VerStrCompare(Copy(s1,nE1+1,Length(s1)),Copy(s2,nE2+1,Length(s2)),pComp)
   else if nV1>nV2 then pComp:=1
   else if nV1<nV2 then pComp:=-1;
  end 
  else pComp:=0;
end;

Usage: VerStrCompare(String1,String2,Result)
where Result=0 if both strings are the same dotted decimal versions.
Result=1 if String1 is newer (higher version) than String2
Result=-1 if String1 is older (lower version) than String2

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Thomas F
  • 21
  • 2