Search code examples
nsis

Compare 'installed-version'versus 'to-be-installed-version'


Have been working with NSIS (v3.05) for a few weeks. Does what it needs to do. I have been chasing the following problem for a few days and have not been able to comprehend it completely. Problem statement: Compare two (2) versions with each other. Many ways to implement. I chose the following: 1st 'version' (string) is retrieved from the currently installed versions .txt file via (function) LineRead (!include TextFunc.nsh) like this:

IfFileExists "C:\$PROGRAMFILES64\...\VERSION.txt" 0 +31 # Open the file and perform N FileRead 
    DetailPrint "VERSION.txt found!"   
    ${LineRead} "C:\$PROGRAMFILES64\...\VERSION.txt" "7" $4  # $4 = '1.x.y.z' (for example)

2nd 'version' is retrieved with following code:

!getdllversion "C:\...\application_name.exe" expversion_
StrCpy $7 ${expversion_}  # pass the define string 'expversion' to $7

As last part I use following code for comparison of $4 and $7:

${VersionCompare} $4 $7 $R0

This can only work as I know for sure that '$4' (version string #1) and '$7' (version string #2) are correct inputs for VersionCompare (output: $R0)

Question: Is there a way to display (for test/check) the content of $4 (@ compile time) in order to know for sure var $4 contains the correct string to be passed to function 'VersionCompare'? (tried 'DetailPrint $4'; does not resolve into the expected string (retrieved by function 'LineRead'). (I know that 'DetailPrint' only displays during executing the installation '.exe. file So is makes sense not seeing that @ compile time.)

Output from MakeNSIS:
IfFileExists: "C:\$PROGRAMFILES64\...\VERSION.txt" ? 0 : +31
DetailPrint: "VERSION.txt found!"
!insertmacro: LineReadCall
!insertmacro: end of LineReadCall
DetailPrint: "The version number of the currently installed app: $4"

How-To get $4 resolve into the version string (for testing purposes) during compile time)?

Question number 2: I use the preprocessor command '!GetDLLVersion' in a function to retrieve version number of the to be installed version of the 'app' (via The NSIS Installer...). MakeNSIS displays resolving the version correctly:

Function: "VersionRetrievalBinary"
!getdllversion: C:\1_SW_dev\...\app.exe (1.8.47.5)->(expversion_<1..4>)

Question: What exactly is 'expverion_'?; a var or a define? If a define (which I read here => Reference/!getdllversion), do I need to define it in my script as follow?:

!define expverion_1 " "  # Major; single digit
!define expverion_2 "  "  # Minor; 2-digit
!define expverion_3 "   " # Build; 3-digit
!define expverion_4 "   "# Revision; 3-digit

Being unsure what 'expversion_' exactly is and operates/works MakeNSIS throws following warning that -I think- indicates clearly something is incorrect:

warning 6000: unknown variable/constant "{expversion_}" detected, ignoring (C:\1_SW_dev\...\app_client.nsi:295)

The worrying part is the word 'ignoring' in the MakeNSIS compiling message. Can I conclude from above mentioned warning message that assigning the string derived from the (define?) with name "expversion_" is not passed to var $7 by means of command:

StrCpy $7 ${expversion_}

Following MakeNSIS message (@ compile time) appears to confirm this:

StrCpy $7 "${expversion_}" () ()

It appears to resolve empty (unsure if I read this MakeNSIS message correctly).

Although having learned a lot wrt NSIS (and liking it) and gone through most of the relevant documentation I do not get my head around this one.

Thnx in advance for solving this specific piece of the puzzle.

Have solved the puzzle myself. The solution to a version comparison is rather 'simple'. The essential parts herewith: (add to .onInit)

  1. Create a function (e.g. name 'VersionComparison')
  2. Read local version from registry
  3. 'Read' installation version from binary (e.g. .DLL or .exe)
  4. Use VersionCompare (!include WordFunc.nsh) to perform the actual comparison.

Solution

  • I'll tackle the !getdllversion issue first. It extracts the 4 16-bit numbers from the start of the version info block and stores them in 4 defines. It simply uses the name you pass in plus a number when naming the defines.

    !getdllversion "$%windir%\explorer.exe" foo
    !warning ${foo2} ; This prints the minor version
    

    There is no specific number of digits as expected by your comments. Each of the 4 numbers go from 0 to 65535.

    You can do basic verification of these numbers at compile time:

    !if ${foo1} < 1
    !error "Major version must at least be 1, we don't ship beta software :) " 
    !endif
    

    The 2nd problem is harder to solve. Variables can only be expanded when the installer is running. The only option is to actually generate and run a "installer" from inside your main .nsi:

    !makensis vertest.nsi = 0
    !execute '"$%temp%\vertest.exe" /S' = 0
    !defile "$%temp%\vertest.exe" 
    

    where vertest.nsi would look something like

    OutFile "$%temp%\vertest.exe" 
    RequestExecutionLevel user
    SilentInstall silent
    
    Section
    Do version test here and Goto.. 
    fail:
    Abort
    success:
    SectionEnd