Compare version strings with InnoSetup

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

7 thoughts on “Compare version strings with InnoSetup”

  1. Nice! But your code does not compile:
    if (GetArrayLength(v1parts) > GetArrayLength(v2parts)) then
    I think > and < are proplematic… ;)

  2. 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.

  3. 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!

  4. 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!

Leave a Reply

Your email address will not be published. Required fields are marked *