views:

504

answers:

4

I want to develop a setup package for conditionally upgrading an existing package. I want to check the existing software version against to-be-installed version. In order to do that, I have to compare the version strings.

How can I convert the string value to a numerical value in a Inno setup script?

RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Blah blah', 'Version', version)
version = 'V1.R2.12';
numVersion := ??string_to_numerical_value??(version);
A: 

I haven't tried that (and my Pascal knowledge is a bit rusty), but something like the following should work:

function NumericVersion(s: String): Integer;
var
  i: Integer;
  s1: String;
begin
  s1 := '';
  for i := 0 to Length(s)-1 do
    if (s[i] >= '0') and (s[i] <= '9') then
      s1 := s1 + s[i];

  Result := StrToIntDef(s1, 0);
end;

Please not that you'll have to play with the start and end value for i as I'm not sure whether it is zero-based or not (s[0] may contain the length of the string if it is a "Pascal String").

Thorsten Dittmar
Pascal strings are one-based, but even with that fixed your solution will fail for corner cases - see my answer.
mghie
You are right of course - I just wanted to point the OP into the right direction. I didn't expect my solution to handle the problem entirely ;-) You'll get an upvote for your answer from me.
Thorsten Dittmar
+2  A: 

This is a little more tricky, as you would want to handle versions like 'V1.R2.12' and 'V0.R15.42' correctly - with the simple conversion in the other answer you would get 1212 and 1542, which would not compare the way you would expect.

You need to decide how big each part of the version number can be, and multiply the parts by that value to get a correct end number. Something like this:

[Code]
function string_to_numerical_value(AString: string; AMaxVersion: LongWord): LongWord;
var
  InsidePart: boolean;
  NewPart: LongWord;
  CharIndex: integer;
  c: char;
begin
  Result := 0;
  InsidePart := FALSE;
  // this assumes decimal version numbers !!!
  for CharIndex := 1 to Length(AString) do begin
    c := AString[CharIndex];
    if (c >= '0') and (c <= '9') then begin
      // new digit found
      if not InsidePart then begin
        Result := Result * AMaxVersion + NewPart;
        NewPart := 0;
        InsidePart := TRUE;
      end;
      NewPart := NewPart * 10 + Ord(c) - Ord('0');
    end else
      InsidePart := FALSE;
  end;
  // if last char was a digit the last part hasn't been added yet
  if InsidePart then
    Result := Result * AMaxVersion + NewPart;
end;

You can test this with the following code:

function InitializeSetup(): Boolean;
begin
  if string_to_numerical_value('V1.R2.12', 1) < string_to_numerical_value('V0.R15.42', 1) then
    MsgBox('Version ''V1.R2.12'' is not as recent as version ''V0.R15.42'' (false)', mbConfirmation, MB_OK);
  if string_to_numerical_value('V1.R2.12', 100) > string_to_numerical_value('V0.R15.42', 100) then
    MsgBox('Version ''V1.R2.12'' is more recent than version ''V0.R15.42'' (true)', mbConfirmation, MB_OK);
  Result := FALSE;
end;

Whether you pass 10, 100 or 1000 for AMaxVersion depends on the number and range of your version number parts. Note that you must not overflow the LongWord result variable, which has a maximum value of 2^32 - 1.

mghie
A: 

I've implemented two version strings (actually one string and one dword value) in the registry to overcome complexity.

displayversion="v1.r1.0"
version="10100"   (=1*10^4 + 1*10^2 + 0*10^0)

That's simple. Though not an answer to this question, however one might think the other way around when faced with complexity, which could be avoided in a simpler way.

A: 

Useful, unless you are comparing versions of apps with values in the version number greater than 10000, like IE and MDAC do.

wilvk

related questions