How to compare two string in NSIS [duplicate] - nsis

This question already has answers here:
How to compare two variables using If Else in NSIS?
(2 answers)
Closed 4 years ago.
I want to compare two strings in NSIS, For example.How to do if else condition for the below code.
ReadRegStr $R0 HKLM "${PRODUCT_UNINST_KEY}" "InstallLocation"
if ;$R0 has some values then it needs to be copied else this " $INSTDIR "$PROGRAMFILES64\${PRODUCT_NAME}""values should be assigned to INSTDIR
StrCpy $INSTDIR "$R0"
else
StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCT_NAME}"

The StrCmp and StrCmpS instructions can be used to compare strings:
StrCmp $myvar "somestring" 0 jump_to_if_not_equal
DetailPrint "myvar was somestring"
goto end
jump_to_if_not_equal:
DetailPrint "not a match"
end:
You can also use the LogicLib helper macros:
!include LogicLib.nsh
${If} $myvar == "something"
DetailPrint "match"
${Else}
DetailPrint "not a match"
${EndIf}

Related

Set run-time cursor position in directory page dialog box - NSIS

I want to add at the end of the path the installation folder name of my App. I do it successfully after clicking the Browse button but, after many tentatives, I couldn't do it if I modify the text directly in the textbox.
Function .onVerifyInstDir
Push $0
FindWindow $0 "#32770" "" $HWNDPARENT
GetDlgItem $0 $0 0x3FB
;in alternative for MUI >2.0 I could use directly $mui.DirectoryPage.Directory. Is it right?
;does path end with "\MyApp"?
StrLen $R1 "\${APP_FOLDER_NAME}"
StrCpy $R0 $INSTDIR "" -$R1
StrCmp $R0 "\${APP_FOLDER_NAME}" +2
;add "\MyApp" after browse button clicking (OK)
StrCpy $INSTDIR "$INSTDIR\${APP_FOLDER_NAME}"
;add "\MyApp" after typing directly into the textbox but the cursor position reset to the first character. Tried to solve saving the current cursor position and then reapply it (NOK)
SendMessage $0 ${EM_GETSEL} null $1
SendMessage $0 ${WM_SETTEXT} 0 "STR:$INSTDIR"
SendMessage $0 ${EM_SETSEL} $1 $1
FunctionEnd
I don't succeed of mantain the cursor where I'm modifying the path string and it reset always to the first char.
In a previous phase of install process I initialise the Install path as follow
StrCpy $INSTDIR "$APPDATA\${APP_FOLDER_NAME}"
NSIS is supposed to do this for you already.
From the docs:
... the part of this string following the last \ will be used if the user selects 'browse', and may be appended back on to the string at install time (to disable this, end the directory with a \ (which will require the entire parameter to be enclosed with quotes). If this doesn't make any sense, play around with the browse button a bit.
Meaning,
InstallDir "$ProgramFiles\MyApp"
is not the same as
InstallDir "$ProgramFiles\MyApp\"
Setting the text in .onVerifyInstDir is not officially supported but this code somewhat works:
!define APP_FOLDER_NAME MyApp
Page Directory
Page InstFiles
!include WinMessages.nsh
!include LogicLib.nsh
Function .onInit
StrCpy $INSTDIR "$APPDATA\${APP_FOLDER_NAME}"
FunctionEnd
Var InOnVerifyInstDir
Var SeenModal
Function .onVerifyInstDir
${IfThen} $InOnVerifyInstDir <> 0 ${|} Return ${|} ; Don't recurse into .onVerifyInstDir
!if ${MUI_SYSVERSION} >= 2.0
StrCpy $0 $mui.DirectoryPage.Directory
!else
FindWindow $0 "#32770" "" $hWndParent
GetDlgItem $0 $0 0x3FB
!endif
System::Call 'USER32::GetActiveWindow()p.r2'
System::Call 'USER32::GetFocus()p.r1'
${If} $1 P<> $0
${If} $hWndParent P<> $2
StrCpy $SeenModal 1
Return
${EndIf}
${If} $SeenModal = 0
Return
${EndIf}
${EndIf}
StrCpy $SeenModal ""
;does path end with "\MyApp"?
StrLen $R1 "\${APP_FOLDER_NAME}"
StrCpy $R0 $INSTDIR "" -$R1
${If} $R0 != "\${APP_FOLDER_NAME}"
StrCpy $InOnVerifyInstDir 1
StrCpy $INSTDIR "$INSTDIR\${APP_FOLDER_NAME}"
SendMessage $0 ${EM_GETSEL} "" "" $1
IntOp $1 $1 >> 16 ; shift hiword
IntOp $1 $1 & 0xffff ; mask possible sign bit
SendMessage $0 ${WM_SETTEXT} 0 "STR:$INSTDIR"
SendMessage $0 ${EM_SETSEL} $1 $1
StrCpy $InOnVerifyInstDir 0
${EndIf}
FunctionEnd
but I would still recommend just using InstallDir.

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

How can I find an application if i don't know its GUID

In order to compare versions, I have to find out if my app has been already installed.
I use registry to store whole necessary inforamtion and it would be very useful if i could somehow read strings from registry. Main issue here is that I don't know my own GUID which was randomized during previous installation.
To generate my registry path I wrote following script:
Function .onInit
${If} ${RunningX64}
StrCpy $R0 "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
${Else}
StrCpy $R0 "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
${EndIf}
FunctionEnd
MainSection:
Section "Main" sec
System::Call 'ole32::CoCreateGuid(g .s)'
Pop $0
WriteRegStr HKLM "$R0\$0" 'DisplayVersion' '${AppVersion}'
SectionEnd
So, basically I need to find a way to read DisplayVersion string. I wish there was some variation of FindFirst but for registry.
Use EnumRegKey to enumerate registry keys:
!include LogicLib.nsh
Section
StrCpy $0 0
loop:
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0
StrCmp $1 "" done
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayName"
${If} $2 == "My Application Name"
ReadRegStr $2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayVersion"
DetailPrint "TODO: Compare $2 to version here..."
${EndIf}
IntOp $0 $0 + 1
Goto loop
done:
SectionEnd

How to compare two variables using If Else in NSIS?

Var first
Var second
Section
Strcpy $first "1.0"
Strcpy $Second "2.1"
${If} $second > $first
MessageBox MB_OK "Grater"
${Else}
MessageBox MB_OK "Smaller"
${EndIf}
SectionEnd
I have written the above code but it is showing me result as smaller. And how to compare a integer or double value coming from a text file with a predefined double or integer value?
Using LogicLib, You may compare two integers like this:
Var first
Var second
Section
StrCpy $first 1
StrCpy $Second 2
${If} $second > $first
MessageBox MB_OK "Grater"
${Else}
MessageBox MB_OK "Smaller"
${EndIf}
SectionEnd
with capital C in StrCpy. Also try removing quotes (") from numbers to make them integers.
Another way would be this:
Push $first
Push $Second
StrCpy $first 8
StrCpy $Second 2
IntCmp $first $Second Equal Val1Less Val1More
Equal:
DetailPrint "$first = $Second"
Goto End
Val1Less:
DetailPrint "$first < $Second"
Goto End
Val1More:
DetailPrint "$first > $Second"
Goto End
End:
Pop $Second
Pop $first
NSIS does not support floating point numbers in the basic instructions, you need to use the Math plugin that is part of the default install...

compound string comparison in nsis

How do I do a compound string comparison in NSIS?
essentially something like: if ( str1 == "" || str2 == "" ) ...
strcpy $1 "c:\foo"
strcpy $2 "d:\bar"
${if} strcmp $1 ""
${orif} strcmp $2 ""
MessageBox MB_OK "one or both are empty"
${else}
messagebox mb_ok "both are not"
${endif}
SectionEnd
StrCmp is the low-level instruction at the heart of NSIS string comparisons but when using the LogicLib you must use the correct operators: ==, !=, S== or S!= (all of them are listed at the top of LogicLib.nsh and the case-insensitive operators use StrCmp internally)
!include LogicLib.nsh
Section
StrCpy $1 "c:\foo"
StrCpy $2 "d:\bar"
${If} $1 == ""
${OrIf} $2 == ""
MessageBox MB_OK "one or both are empty"
${Else}
MessageBox MB_OK "both are not"
${EndIf}
SectionEnd

Resources