I recently had to implement version string comparison for InnoSetup, because the solutions that I found on the internet did not suit my needs. Hopefully this will be useful for somebody else.
// Procedure to split a string into an array of integers procedure Explode(var Dest: TArrayOfInteger; Text: String; Separator: String); var i, p: Integer; begin i := 0; repeat SetArrayLength(Dest, i+1); p := Pos(Separator,Text); if p > 0 then begin Dest[i] := StrToInt(Copy(Text, 1, p-1)); Text := Copy(Text, p + Length(Separator), Length(Text)); i := i + 1; end else begin Dest[i] := StrToInt(Text); Text := ''; end; until Length(Text)=0; end; // Function compares version strings numerically: // * when v1 = v2, result = 0 // * when v1 < v2, result = -1 // * when v1 > v2, result = 1 // // Supports version numbers with trailing zeroes, for example 1.02.05. // Supports comparison of two version number of different lengths, for example // CompareVersions('1.2', '2.0.3') // When any of the parameters is '' (empty string) it considers version number as 0 function CompareVersions(v1: String; v2: String): Integer; var v1parts: TArrayOfInteger; v2parts: TArrayOfInteger; i: Integer; begin if v1 = '' then begin v1 := '0'; end; if v2 = '' then begin v2 := '0'; end; Explode(v1parts, v1, '.'); Explode(v2parts, v2, '.'); if (GetArrayLength(v1parts) > GetArrayLength(v2parts)) then begin SetArrayLength(v2parts, GetArrayLength(v1parts)) end else if (GetArrayLength(v2parts) > GetArrayLength(v1parts)) then begin SetArrayLength(v1parts, GetArrayLength(v2parts)) end; for i := 0 to GetArrayLength(v1parts) - 1 do begin if v1parts[i] > v2parts[i] then begin { v1 is greater } Result := 1; exit; end else if v1parts[i] < v2parts[i] then begin { v2 is greater } Result := -1; exit; end; end; { Are Equal } Result := 0; end;
To test the code in InnoSetup, you can use the following function:
procedure TestVersions(v1: String; v2: String); begin Log(v1 + ' : ' + v2 + ' = ' + IntToStr(CompareVersions(v1, v2))); end;
And call it with some parameters:
TestVersions('1', '2'); TestVersions('2', '1'); TestVersions('3', '3'); TestVersions('1.1', '1'); TestVersions('2.1', '1'); TestVersions('1.1', '2'); TestVersions('2.12.11', '2.12.25'); TestVersions('', '2.12.25'); TestVersions('2.12.25', ''); TestVersions('', ''); TestVersions('2.12.11', '2.012.11');
A practical example can be used as follows:
if CompareVersion(currentVersion, newVersion) = -1 then begin //do the upgrade end
Nice! But your code does not compile:
if (GetArrayLength(v1parts) > GetArrayLength(v2parts)) then
I think > and < are proplematic… ;)
This code compiles for me on Inno 5.5.2 (U). The only bits that didn’t were the “>” and “<” that probably were substituted by this page when the code was copied in.
I meant
">" and
"<".
Trying again: <; and >; .
The > and < were not compiling.
So anyway, Arvydas, thanks for your work on this solution. I’ll be modifying it for my use in Inno, and you’ve probably saved me a good bit of work!
In the Explode function, I substituted:
Text := Copy(Text, p + Length(Separator), Length(Text)-((p-1)+Length(Separator)) );
for
Text := Copy(Text, p + Length(Separator), Length(Text));
because Separator is a String and you allow for its length when you calculate the index to start copying from, so you also have to use its length when calculating the number of characters to copy.
Cheers!