visual-c++nsisvisual-c++-2005

How to get the correct return value/status of VC++ 2005 in NSIS


I want to silently install Visual C++ Redistributable Packages for Visual Studio 2013 with NSIS.

However, the correct exit code is not being returned. I tested with several types of variations. The best I came up with is:

DetailPrint "Installing VC++ 2005"
    File "${STOCKINPUTFOLDER}\bin\Debug\vcredist_x86.EXE"
    ClearErrors
    ExecWait "$OUTDIR\vcredist_x86.EXE /q:a /c:$\"msiexec /i vcredist.msi /qb /norestart$\"" $0 ; also can use /qn 

    ;do not delete the file out-rightly so that evaluation of IfErrors below is clean           
    IfErrors execError noError

    execError:
        Goto ShortEndInstall

    ShortEndInstall:
        MessageBox MB_OK|MB_ICONEXCLAMATION "ShortEndInstall"
        Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean
        Quit

    noError:
        ${If} $0 != 0
            MessageBox MB_OK|MB_ICONEXCLAMATION "You did not complete the installation of the VC++ 2005 re-distributable. Installation will now abort. Return code $0"
            Goto ShortEndInstall
        ${EndIf}

        MessageBox MB_OK|MB_ICONEXCLAMATION "No error"
        Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean

But when I intentionally clicked on the "Cancel" button of the VC++2005 installation, NSIS still took it as being successful installation (I got the dialog box at instruction MessageBox MB_OK|MB_ICONEXCLAMATION "No error")

How can I accurately and correctly get the returned error (e.g. when I "Cancel"ed the installation myself)?

EDIT:

Following the help by @Anders I now have the following, which still fails (i.e. not installed even while the VC++ 2005 installation was completed. I also tried different command line options for the installation VC++2005)

!include LogicLib.nsh
!include WordFunc.nsh

!define VERSION "8.1.0.0"
!define SHORTVERSION "8.1"
!define NAME "SchoolServer MainHost"
!define SHARPDEVFOLDER "..\..\..\.."
!define STOCKINPUTFOLDER "${SHARPDEVFOLDER}\SchoolServer_100\SS-Server"
!define PARENTSTOCKFOLDER "${SHARPDEVFOLDER}\SchoolServer_100"

!define MSVC2005_X86REDIST_PRODUCTCODE {A49F249F-0C91-497F-86DF-B2585E8E76B7}
!define MSVC2005_X86REDIST_PRODUCTCODE_SP1 {7299052B-02A4-4627-81F2-1818DA5D550D}
!define MSVC2005_X86REDIST_PRODUCTCODE_SP1_UP {837B34E3-7C30-493C-8F6A-2B0F04E2912C}
!define INSTALLSTATE_DEFAULT 5

!define MACRO_check_successful_install "!insertmacro check_vc_install"
!define MACRO_check_successful_install_exclusive_2005 "!insertmacro check_vc_install_exclusive_2005"

Var function_call_return
Var tested_vc_key

!macro check_vc_install install_key
  DetailPrint "Copying  ${install_key} to tested_vc_key"
  StrCpy $tested_vc_key ${install_key}
  call check_vc_install_normal
!macroend

!macro check_vc_install_exclusive_2005 install_key
  StrCpy $tested_vc_key ${install_key}
  call check_vc_install_exclusive_2005
!macroend

OutFile "${NAME} Lite Installer - ${SHORTVERSION}.exe"

#Sections
Section "Installation"
    SetShellVarContext all
    SetOutPath "$INSTDIR"
    SetRebootFlag false
    DetailPrint "Installing VC++ 2005"
    File "${STOCKINPUTFOLDER}\bin\Debug\vcredist_x86.EXE"
    ClearErrors
    ExecWait "$OUTDIR\vcredist_x86.EXE /q:a /c:$\"msiexec /i vcredist.msi /qb /norestart$\" " $0 ; also can use /qn 

    ;we will not delete the file out-rightly so that evaluation of IfErrors below is clean      
    IfErrors execError noError

    execError:
        MessageBox MB_OK|MB_ICONEXCLAMATION "First line error!"
        Goto ShortEndInstall

    ShortEndInstall:
    MessageBox MB_OK|MB_ICONEXCLAMATION "Installation of Microsoft Visual C++ 2005 runtime libraries failed!"
    Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean
    ;Quit
    Return

    noError:
    ;if no error, then check the exit code (https://sourceforge.net/p/nsis/mailman/message/22965694/ [use timemachine if 404 error. Page was accessed June 26, 2017])
    ${If} $0 != 0
        MessageBox MB_OK|MB_ICONEXCLAMATION "You did not complete the installation of the VC++ 2005 re-distributable. Installation will now abort. [Error $0]"
        Goto ShortEndInstall
    ${EndIf}

    ClearErrors
    ${MACRO_check_successful_install} ${MSVC2005_X86REDIST_PRODUCTCODE}
    ${If} $function_call_return == "installed"
        Goto ContinueInstallation
    ${Else}
        ${MACRO_check_successful_install} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1}
        ${If} $function_call_return == "installed"
            Goto ContinueInstallation
        ${Else}
            ${MACRO_check_successful_install} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1_UP}
            ${If} $function_call_return == "installed"
                Goto ContinueInstallation
            ${Else}
                ${MACRO_check_successful_install_exclusive_2005} ${MSVC2005_X86REDIST_PRODUCTCODE}
                ${If} $function_call_return == "installed"
                    Goto ContinueInstallation
                ${Else}
                    ${MACRO_check_successful_install_exclusive_2005} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1}
                    ${If} $function_call_return == "installed"
                        Goto ContinueInstallation
                    ${Else}
                        ${MACRO_check_successful_install_exclusive_2005} ${MSVC2005_X86REDIST_PRODUCTCODE_SP1_UP}
                        ${If} $function_call_return == "installed"
                            Goto ContinueInstallation
                        ${Else}
                            DetailPrint "Quiting... [Returned value: $function_call_return]"
                            Goto ShortEndInstall
                        ${EndIf}
                    ${EndIf}
                ${EndIf}
            ${EndIf}
        ${EndIf}
    ${EndIf}

    ContinueInstallation:

    Delete "$OUTDIR\vcredist_x86.EXE" ;we did not delete the file outrightly so that evaluation of IfErrors is clean

    CreateDirectory  "$INSTDIR\idcard"
    File /r "${STOCKINPUTFOLDER}\bin\Debug\idcard" 
SectionEnd


Function check_vc_install_normal
    DetailPrint "check_vc_install_normal: Checking installation of $tested_vc_key"
    System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "ProductName", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
    ClearErrors
    ${If} $0 == 0
        DetailPrint "ProductName: $1"
        System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "AssignmentType", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
        DetailPrint "AssignmentType: $1"
        System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "PackageCode", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
        DetailPrint "PackageCode: $1"
        System::Call 'MSI::MsiGetProductInfo(t "$tested_vc_key", t "VersionString", t"?"r1, *i${NSIS_MAX_STRLEN})i.r0'
        DetailPrint "VersionString: $1"
        StrCpy $function_call_return "installed"
    ${Else}
        DetailPrint "Not installed [$0] [from check_vc_install_normal]"
        StrCpy $function_call_return "not installed"
    ${EndIf}
FunctionEnd

Function check_vc_install_exclusive_2005
    DetailPrint "check_vc_install_exclusive_2005: Checking special vc2005 installation of $tested_vc_key"
    ClearErrors
    System::Call 'MSI::MsiQueryProductState(t "$tested_vc_key")i.r0'
    ${If} ${INSTALLSTATE_DEFAULT} = $0
        StrCpy $function_call_return "installed"
    ${Else}
        DetailPrint "Not installed [$0] [from check_vc_install_exclusive_2005]"
        StrCpy $function_call_return "not installed"
    ${EndIf}
FunctionEnd

Solution

  • Perhaps vcredist_x86.exe is not returning the exit code correctly from MsiExec?

    If ExecWait is able to find and start the process (IfErrors is "false") then NSIS is guaranteed to return the correct exit code from the child process.

    $0 != 0 is actually a string comparison and you should be using $0 <> 0 to compare 32-bit numbers but it is not the problem in this case.

    !include LogicLib.nsh
    Section
    
    ExecWait '"cmd" /c exit 0' $0
    ${If} ${Errors}
    ${OrIf} $0 <> 0
        Abort "Error, child process exited with $0" # Will not abort here
    ${EndIf}
    
    ExecWait '"cmd" /c exit 1234' $0
    ${If} ${Errors}
    ${OrIf} $0 <> 0
        Abort "Error, child process exited with $0" # Will abort here
    ${EndIf}
    
    SectionEnd