I am looking for a NSIS example that is able to update itself with the latest version of the installer.
The logic is simple: check if the publish URL is newer than the current executable and download it and recall itself with the same parameters.
Mainly that's would me something like "wget --timestamp URL" but so far I wasn't able to find any NSIS file download extension that is able to download a file only if is newer.
INetC::head will grab just the HTTP headers from the server but IMHO downloading a .ini file with version info from the server is better...
Here's something I wrote that might be of help to you. I have not tested this thoroughly but it did work on the first couple tests I ran. This is just an example I threw together for a project I'm working on. Hope it's of use to you. =)
VersionCompare Function
;=#
;= Taken and modified from:
;= http://nsis.sourceforge.net/VersionCompare
Function VersionCompare
!define VersionCompare `!insertmacro _VersionCompare`
!macro _VersionCompare _VER1 _VER2 _RESULT
Push `${_VER1}`
Push `${_VER2}`
Call VersionCompare
Pop ${_RESULT}
!macroend
Exch $1
Exch
Exch $0
Exch
Push $2
Push $3
Push $4
Push $5
Push $6
Push $7
BEGIN:
StrCpy $2 -1
IntOp $2 $2 + 1
StrCpy $3 $0 1 $2
StrCmp $3 '' +2
StrCmp $3 '.' 0 -3
StrCpy $4 $0 $2
IntOp $2 $2 + 1
StrCpy $0 $0 '' $2
StrCpy $2 -1
IntOp $2 $2 + 1
StrCpy $3 $1 1 $2
StrCmp $3 '' +2
StrCmp $3 '.' 0 -3
StrCpy $5 $1 $2
IntOp $2 $2 + 1
StrCpy $1 $1 '' $2
StrCmp $4$5 '' EQUAL
StrCpy $6 -1
IntOp $6 $6 + 1
StrCpy $3 $4 1 $6
StrCmp $3 '0' -2
StrCmp $3 '' 0 +2
StrCpy $4 0
StrCpy $7 -1
IntOp $7 $7 + 1
StrCpy $3 $5 1 $7
StrCmp $3 '0' -2
StrCmp $3 '' 0 +2
StrCpy $5 0
StrCmp $4 0 0 +2
StrCmp $5 0 BEGIN NEWER2
StrCmp $5 0 NEWER1
IntCmp $6 $7 0 NEWER1 NEWER2
StrCpy $4 '1$4'
StrCpy $5 '1$5'
IntCmp $4 $5 BEGIN NEWER2 NEWER1
EQUAL:
StrCpy $0 0
Goto FINISH
NEWER1:
StrCpy $0 1
Goto FINISH
NEWER2:
StrCpy $0 2
FINISH:
Pop $7
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Exch $0
FunctionEnd
UpdateCheck Function
;=#
;= Author: demon.devin
;= http://softables.tk/
Function UpdateCheck
!define UpdateCheck `!insertmacro _UpdateCheck`
!macro _UpdateCheck _NAME _LINK _URL _VERSION
Push `${_URL}`
Push `${_VERSION}`
Call UpdateCheck
Pop `${_NAME}`
Pop `${_LINK}`
!macroend
Exch $1
Exch
Exch $0
Push $0
Push $1
DetailPrint "Current Version: $1"
GetTempFileName $R1
NSISdl::download $0 $R1
ReadINIStr $R0 "$R1" "PAFInfo" "Version"
DetailPrint "Latest Version: $R0"
${VersionCompare} $1 $R0 $R4
${If} $R4 == 0
${OrIf} $R4 == 1
ReadINIStr $R3 "$R1" "PAFInfo" "Name"
StrCpy $File `$R3`
StrCpy $R2 ""
StrCpy $Link ``
${Else}
ReadINIStr $R2 "$R1" "PAFInfo" "DownloadLink"
StrCpy $Link `$R2`
ReadINIStr $R3 "$R1" "PAFInfo" "Filename"
StrCpy $File `$R3`
${EndIf}
SetDetailsPrint none
Delete $R1
SetDetailsPrint lastused
Pop $1
Pop $0
Exch $R2
Exch
Exch $R3
FunctionEnd
WGetDownload Function
;=#
;= Author: demon.devin
;= http://softables.tk/
Function WGetDownload
!define WGetDownload "!insertmacro _WGetDownload"
!macro _WGetDownload _RETURN _TEMPFILE _URL _FILENAME
Push ${_URL}
Push ${_FILENAME}
Call WGetDownload
Pop ${_TEMPFILE}
Pop ${_RETURN}
!macroend
Exch $1
Exch
Exch $2
Push $1
Push $2
!define WGET `$PLUGINSDIR\wget.exe`
!define WGETCMD `"${WGET}" --html-extension --referer="http://www.google.com" --user-agent="Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6" -T 10 --no-check-certificate "$Link" --output-document=$File`
MessageBox MB_OK|MB_USERICON|MB_TOPMOST "Package: $File$\r$\nLink: $Link$\r$\n$\r$\nClick OK to start download."
DetailPrint "Downloading: $Link"
SetOutPath `$PLUGINSDIR`
File `${NSISDIR}\Contrib\7zip\wget.exe`
WGET:
SetOutPath `$PLUGINSDIR`
ExecDos::Exec /TOSTACK `${WGETCMD}`
Pop $0
Pop $1
${If} $0 = 1
StrCmp $2 1 +3
StrCpy $2 1
Goto WGET
Goto FAIL
${EndIf}
${If} ${FileExists} "$PLUGINSDIR\$File"
ClearErrors
StrCpy $R5 true
StrCpy $R7 "$PLUGINSDIR\$File"
${GetSize} "$PLUGINSDIR" "/M=$File /S=0K" $0 $1 $2
IfErrors 0 +2
StrCpy $0 $0 * 1024
IntCmp $0 100 +3 0 +3
IntCmp $0 0 +2 +2 0
IntOp $0 103 + 0
IntOp $0 $0 * 10
IntOp $0 $0 / 1024
StrCpy $1 "$0" "" -1
IntCmp $0 9 +3 +3 0
StrCpy $0 "$0" -1 ""
Goto +2
StrCpy $0 "0"
MessageBox MB_OK|MB_USERICON|MB_TOPMOST "Your file $File was successfully downloaded! ($0.$1 MiB)"
Goto DONE
${Else}
FAIL:
StrCpy $R5 "Your file $File has failed to download using the WGet utility!"
Goto CANCEL
${EndIf}
CANCEL:
MessageBox MB_OK|MB_ICONSTOP|MB_TOPMOST "ERROR!$\r$\n$\r$\n$R5"
StrCpy $R5 false
StrCpy $R7 ""
SetErrors
DONE:
Pop $2
Pop $1
Exch $R5
Exch
Exch $R7
FunctionEnd
OpenWebPage Macro
;=#
;= Author: demon.devin
;= http://softables.tk/
!define OpenWebPage '!insertmacro "_OpenWebPage"'
!macro _OpenWebPage _URL
ExecShell open "${_URL}"
!macroend
EXAMPLE USAGE:
;=#
;= Author: demon.devin
;= More NSIS help:
;= http://softables.tk/docs
Section "MAIN"
ClearErrors
MessageBox MB_USERICON|MB_YESNO|MB_TOPMOST `Would you like to check for any possible upgrades?` IDYES CHECK IDNO NOCHECK
NOCHECK:
DetailPrint "What?! How can you improve if you don't check for upgrades?"
Goto DONE
CHECK:
DetailPrint "Checking for new version..."
${UpdateCheck} $0 $1 "http://softables.tk/repository/docs/manifests.ini" "6.25.11.0"
StrCmp $1 "" 0 +5
DetailPrint "You have the most recent version..."
MessageBox MB_USERICON|MB_OK|MB_TOPMOST `$0 is up to date!`
DetailPrint "Continuing with the rest of the operation..."
Goto DONE
DetailPrint "There's a new version available..."
MessageBox MB_ICONEXCLAMATION|MB_YESNO|MB_TOPMOST "New version of $File available.$\r$\nDownload now?" IDYES YES IDNO NO
NO:
DetailPrint "Fine! Stick with the old stuff. See if I care..."
Goto DONE
YES:
DetailPrint "Great! Downloading new updates now..."
${WGetDownload} $R0 $R1 "$Link" "$File"
${If} $R0 == true
${AndIf} $R1 != ""
DetailPrint "Downloaded: $R1"
DetailPrint "Awesome! The download was successful..."
CopyFiles /SILENT `$R1` `$INSTDIR\WGET\$R1`
DetailPrint "Extracting the contents now..."
File '/oname=$PLUGINSDIR\7z.dll' `${NSISDIR}\Contrib\7zip\7z.dll`
File '/oname=$PLUGINSDIR\7z.exe' `${NSISDIR}\Contrib\7zip\7z.exe`
ExecDos::Exec /TOSTACK `"$PLUGINSDIR\7z.exe" x "$R1" -o"$INSTDIR" -aoa -y` "" ""
Pop $0
DetailPrint "All done! Cleaning up temporary files..."
Delete `$R1`
DetailPrint "Finished. Enjoy..."
${Else}
DetailPrint "Something went wrong! The download was unsuccessful..."
MessageBox MB_ICONEXCLAMATION|MB_YESNO|MB_TOPMOST "You can manually download the upgrades by visiting:$\r$\n$Link$\r$\n$\r$\nGo there now?" IDYES WEB IDNO DONE
WEB:
DetailPrint "Launching web page now... "
SetDetailsPrint none
${OpenWebPage} "$Link"
SetDetailsPrint lastused
${EndIf}
DONE:
DetailPrint "Moving right along..."
SectionEnd
Related
My installer working fine without nsdialogs, If i add nsdialogs its not working good.I can't call nsdialogs in correct place.where i need to call nsdialogs?
Var hwnd
Var Dialog
Page custom checkinstall
Page custom nsDialogsPage
.
.
.
Function .onInit
!define MUI_LANGDLL_ALWAYSSHOW
!insertmacro MUI_LANGDLL_DISPLAY
SetShellVarContext all
!insertmacro VerifyUserIsAdmin
FunctionEnd
Function un.onInit
!insertmacro MUI_UNGETLANGUAGE
SetShellVarContext all
SetRebootFlag true
MessageBox MB_OKCANCEL "Are you sure you want to uninstall
${APPNAME}?" IDOK next
Abort
next:
!insertmacro VerifyUserIsAdmin
FunctionEnd
Function un.onUninstSuccess
HideWindow
MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed
from your computer."
FunctionEnd
Function Repair
Call .onInit
FunctionEnd
Function Remove
ExecWait "$INSTDIR\Uninstall.exe"
FunctionEnd
Function checkinstall
ReadRegStr $R0 HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString"
IfFileExists $R0 +1 NotInstalled
Call nsDialogsPage
NotInstalled:
FunctionEnd
Function nsDialogsPage
nsDialogs::Create 1018
Pop $Dialog
${If} $Dialog == error
Abort
${EndIf}
${NSD_CreateRadioButton} 0 5u 100% 10u "Repair"
Pop $hwnd
${NSD_AddStyle} $hwnd ${WS_GROUP}
${NSD_OnClick} $hwnd Repair
${NSD_CreateRadioButton} 0 25u 100% 56u "Remove"
Pop $hwnd
${NSD_OnClick} $hwnd Remove
nsDialogs::Show
FunctionEnd
I can not call nsdialogs correctly. checkinstall function not working at all.I need a correct solution for that.
You are writing the uninstall command to the registry with quotes (correct) but you are not removing the quotes when reading the value so the file exists test is always going to be false.
You should remove the quotes from commands you read from the registry:
Function GetAppFromCommand
Exch $1
Push $2
Push $3
StrCpy $3 ""
StrCpy $2 $1 1
StrCmp $2 '"' qloop sloop
sloop:
StrCpy $2 $1 1 $3
IntOp $3 $3 + 1
StrCmp $2 "" +2
StrCmp $2 ' ' 0 sloop
IntOp $3 $3 - 1
Goto done
qloop:
StrCmp $3 "" 0 +2
StrCpy $1 $1 "" 1 ; Remove initial quote
IntOp $3 $3 + 1
StrCpy $2 $1 1 $3
StrCmp $2 "" +2
StrCmp $2 '"' 0 qloop
done:
StrCpy $1 $1 $3
Pop $3
Pop $2
Exch $1
FunctionEnd
!macro GetAppFromCommand cmd outvar
Push `${cmd}`
Call GetAppFromCommand
Pop ${outvar}
!macroend
Function checkinstall
ReadRegStr $R0 HKLM \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
"UninstallString"
!insertmacro GetAppFromCommand $R0 $R0
IfFileExists $R0 +1 NotInstalled
Call nsDialogsPage
NotInstalled:
FunctionEnd
I have tried a lot of possibility with this code but it doesn't work. I need to check a VPN connection. If I run the code firs time it works, but if I don't restart the program and push the button secondly then the program just disappear. Any idea how to fix it? thanks.
Function VpnLoginWindow
${Do}
!insertmacro VpnLoginWindow
StrCpy $4 0
${If} $3 == 1
${Do}
!insertmacro VpnLoginWindow
StrCpy $4 1
StrCmp $3 "0" 0 +2
${ExitDo}
${Loop}
${EndIf}
${If} $4 == 1
${ExitDo}
${EndIf}
${Loop}
FunctionEnd
!macro VpnLoginWindow
StrCpy $5 $1
System::Get "(i.r1) iss"
Pop $R0
System::Call "user32::EnumWindows(k R0, i) i.s"
${Do}
Pop $0
StrCmp $0 "1" 0 +2
${ExitDo}
System::Call "user32::GetWindowText(ir1,t.r2,i ${NSIS_MAX_STRLEN})"
StrCpy $2 $2 7
StrCpy $3 0
${If} $2 == "SSL VPN"
StrCpy $3 1
${ExitDo}
${EndIf}
Push 1 # callback's return value
System::Call "$R0"
${Loop}
System::Free $R0
StrCpy $1 $5
!macroend
Callbacks are tricky to get right and the plug-in is a little buggy. Also, you should not use relative jumps to jump over macros like ${ExitDo}!
!include LogicLib.nsh
!macro FindVpnLoginWindow
Push "" ; Result: Window not found
System::Store S
System::Get '(p.r1, p)ir0r0'
Pop $9
System::Call 'USER32::EnumWindows(k r9, p 0)'
${Do}
${IfThen} $0 != "callback1" ${|} ${ExitDo} ${|} ; <-- adjust the callback# if required
StrCpy $0 1 ; Set callback return value, continue search
System::Call "USER32::GetWindowText(pr1, t.r2, i ${NSIS_MAX_STRLEN})"
StrCpy $2 $2 7
${If} $2 == "SSL VPN"
Pop $2 ; Throw away old result
Push $1 ; Result: HWND
StrCpy $0 0 ; Set callback return value, stop enum with 0
${EndIf}
System::Call $9 ; Return from callback function
${Loop}
System::Free $9
System::Store L
!macroend
Function BackgroundFindWindow
!insertmacro FindVpnLoginWindow
Pop $0
DetailPrint "Result:$0"
FunctionEnd
...
GetFunctionAddress $0 BackgroundFindWindow
BgWorker::CallAndWait
GetFunctionAddress $0 BackgroundFindWindow
BgWorker::CallAndWait
When passing a single argument to my windows installer,
MyApplication.exe CLIENT="Your Mom"
I can correctly read it using GetParameters and GetOptions, like so:
${GetParameters} $0
${GetOptions} "$0" "CLIENT=" $CLIENT
But as soon as I attempt to pass multiple parameters
MyApplication.exe CLIENT="Your Mom" NAME="Jill" LOCATION="Da Yard" TEL="0221456789"
and try to read it like so
${GetParameters} $0
${GetOptions} "$0" "CLIENT=" $CLIENT
${GetOptions} "$0" "NAME=" $NAME
${GetOptions} "$0" "LOCATION=" $LOCATION
${GetOptions} "$0" "TEL=" $TEL
The values of $CLIENT, $NAME and $TEL would be correct, but $LOCATION will contain
Da Yard TEL=0221456789
The same happens if I add more parameters, the first two and last one is always correct, but the parameters in between always contains some substring of the string passed to the installer.
Am I using GetOptions correctly?
If you change all the parameters so they start with / then it works for some reason! (MyApplication.exe /CLIENT="Your Mom" /NAME="Jill" /LOCATION="Da Yard" /TEL="0221456789" and ${GetOptions} "$0" "/LOCATION=" $LOCATION)
If you cannot live with the / switch prefix then you would have to rewrite ${GetOptions} yourself or wait until the next NSIS release where it will hopefully be fixed.
If you want to take a stab at fixing it yourself then it would look something like this:
!include FileFunc.nsh
!macroundef GetOptionsBody
!macro GetOptionsBody _FILEFUNC_S
Exch $1 ; Prefix
Exch
Exch $0 ; String
Exch
ClearErrors
; Parse $0 here and look for $1 and store the suffix in $0...
FileFunc_GetOptions${_FILEFUNC_S}_notfound:
SetErrors
StrCpy $0 ''
FileFunc_GetOptions${_FILEFUNC_S}_end:
Pop $1
Exch $0
!macroend
Edit:
It seems like this might be by design, the help file contains this little broken English tidbit:
First option symbol it is delimiter
Edit2:
I made a new GetOptions from scratch, it only supports double quotes and is probably different in all sorts of ways. I did not test much but it seems to work OK:
Outfile "Test.exe"
RequestExecutionLevel user
ShowInstDetails show
!include FileFunc.nsh
!if '${NSIS_PACKEDVERSION}' <= 0x0300003f ; Older versions don't support !macroundef
!define GetOptionsBody_Alt GetOptionsBody_Alt
!undef GetOptions
!define GetOptions `!insertmacro GetOptionsCall_Alt`
!macro GetOptionsCall_Alt _PARAMETERS _OPTION _RESULT
!verbose push
!verbose ${_FILEFUNC_VERBOSE}
Push `${_PARAMETERS}`
Push `${_OPTION}`
${CallArtificialFunction} GetOptions_Alt_
Pop ${_RESULT}
!verbose pop
!macroend
!macro GetOptions_Alt_
!verbose push
!verbose ${_FILEFUNC_VERBOSE}
!insertmacro ${GetOptionsBody_Alt} ''
!verbose pop
!macroend
!else
!define GetOptionsBody_Alt GetOptionsBody
!macroundef ${GetOptionsBody_Alt}
!endif
!macro ${GetOptionsBody_Alt} _FILEFUNC_S ; This alternative version only knows about " quotes and assumes there is nothing or a space/tab before the prefix
Exch $1 ; Prefix
Exch
Exch $0 ; String
Exch
Push $2 ; The quote type we are in if any (Currently only supports ")
Push $3 ; Position in $0
Push $4 ; Temp
Push $5 ; Temp
Push $6 ; Start of data
ClearErrors
StrCpy $2 ''
StrCpy $3 "-1"
StrCpy $6 "-1"
FileFunc_GetOptions${_FILEFUNC_S}_loop:
StrCpy $5 $0 1 $3
IntOp $3 $3 + 1
StrCpy $4 $0 1 $3
StrCmp $4 "" FileFunc_GetOptions${_FILEFUNC_S}_eos
StrCmp $4 '"' FileFunc_GetOptions${_FILEFUNC_S}_foundquote
StrCmp $2 '' 0 FileFunc_GetOptions${_FILEFUNC_S}_loop ; We are inside a quote, just keep looking for the end of it
StrCmp -1 $6 0 FileFunc_GetOptions${_FILEFUNC_S}_dataisunquoted ; Have we already found the prefix and start of data?
IntCmpU $3 0 +2 ; $3 starts as -1 so $5 might contain the last character so we force it to a space
StrCmp $5 '$\t' 0 +2
StrCpy $5 " "
StrCmp $5 " " 0 FileFunc_GetOptions${_FILEFUNC_S}_loop ; The prefix must be at the start of the string or be prefixed by space or tab
StrLen $4 $1
StrCpy $5 $0 $4 $3
StrCmp${_FILEFUNC_S} "$5" "$1" "" FileFunc_GetOptions${_FILEFUNC_S}_loop
IntOp $6 $4 + $3 ; Data starts here
IntOp $3 $6 - 1 ; This is just to ignore the + 1 at the top of the loop
Goto FileFunc_GetOptions${_FILEFUNC_S}_loop
FileFunc_GetOptions${_FILEFUNC_S}_dataisunquoted:
StrCmp $4 ' ' FileFunc_GetOptions${_FILEFUNC_S}_extractdata
StrCmp $4 '$\t' FileFunc_GetOptions${_FILEFUNC_S}_extractdata FileFunc_GetOptions${_FILEFUNC_S}_loop
FileFunc_GetOptions${_FILEFUNC_S}_extractdata:
IntOp $5 $3 - $6
StrCpy $0 $0 $5 $6
Goto FileFunc_GetOptions${_FILEFUNC_S}_return
FileFunc_GetOptions${_FILEFUNC_S}_foundquote:
StrCmp $2 $4 FileFunc_GetOptions${_FILEFUNC_S}_endquote
StrCpy $2 $4 ; Starting a quoted part
Goto FileFunc_GetOptions${_FILEFUNC_S}_loop
FileFunc_GetOptions${_FILEFUNC_S}_endquote:
StrCpy $2 ''
StrCmp -1 $6 FileFunc_GetOptions${_FILEFUNC_S}_loop FileFunc_GetOptions${_FILEFUNC_S}_extractquoteddata
FileFunc_GetOptions${_FILEFUNC_S}_eos: ; End Of String
StrCmp $2 '' +2
FileFunc_GetOptions${_FILEFUNC_S}_extractquoteddata:
IntOp $6 $6 + 1 ; Skip starting quote when extracting the data
StrCmp -1 $6 0 FileFunc_GetOptions${_FILEFUNC_S}_extractdata
SetErrors
StrCpy $0 ''
FileFunc_GetOptions${_FILEFUNC_S}_return:
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Exch $0
!macroend
Var CLIENT
Var NAME
Var LOCATION
Var TEL
Function Test
${GetParameters} $0
DetailPrint "Calling GetOptions on |$0|"
${GetOptions} $0 "/CLIENT=" $CLIENT
${GetOptions} $0 "/NAME=" $NAME
${GetOptions} $0 "/LOCATION=" $LOCATION
${GetOptions} $0 "/TEL=" $TEL
DetailPrint "/CLIENT=|$CLIENT|"
DetailPrint "/NAME=|$NAME|"
DetailPrint "/LOCATION=|$LOCATION|"
DetailPrint "/TEL=|$TEL|"
${GetOptions} $0 "CLIENT=" $CLIENT
${GetOptions} $0 "NAME=" $NAME
${GetOptions} $0 "LOCATION=" $LOCATION
${GetOptions} $0 "TEL=" $TEL
DetailPrint "CLIENT=|$CLIENT|"
DetailPrint "NAME=|$NAME|"
DetailPrint "LOCATION=|$LOCATION|"
DetailPrint "TEL=|$TEL|"
FunctionEnd
Section
; Normally I would detect this automation with a command line parameter but
; because we are debugging those I'm using this hack instead
ExpandEnvStrings $0 "%NSIS_Test_GetOptions%"
StrCmp $0 "1337" 0 launchselfwithparams
Call Test
Goto done
launchselfwithparams:
System::Call 'KERNEL32::SetEnvironmentVariable(t "NSIS_Test_GetOptions", t "1337")'
DetailPrint "NSIS ${NSIS_VERSION}"
ExecWait '"$ExePath" /CLIENT="Your Mom"'
ExecWait '"$ExePath" /CLIENT="Your Mom" /NAME="Jill" /LOCATION="Da Yard" /TEL="0221456789"'
ExecWait '"$ExePath" CLIENT="Your Mom"'
ExecWait '"$ExePath" CLIENT="Your Mom" NAME="Jill" LOCATION="Da Yard" TEL="0221456789"'
${GetOptions} 'foo=bar bar=baz baz=biz' 'bar=' $R0
DetailPrint |$R0|
${GetOptions} 'foo=bar bar=baz baz=biz' 'failthis=' $R0
DetailPrint |$R0|
${GetOptions} '/SILENT=yes/INSTDIR=bug /INSTDIR="C:/Program Files/Common Files" /ADMIN=password' "/INSTDIR=" $R0
DetailPrint |$R0|
${GetOptions} 'SILENT=yesINSTDIR=bug INSTDIR="C:/Program Files/Common Files" ADMIN=password' "INSTDIR=" $R0
DetailPrint |$R0|
${GetOptions} '/SILENT="yes/INSTDIR=bug" /INSTDIR="C:/Program Files/Common Files" /ADMIN="password"' "/INSTDIR=" $R0
DetailPrint |$R0|
${GetOptions} 'SILENT="yesINSTDIR=bug" INSTDIR="C:/Program Files/Common Files" ADMIN="password"' "INSTDIR=" $R0
DetailPrint |$R0|
done:
SectionEnd
I'm using CPU Features plug-in to get CPU core and this is the code:
${CPUFeatures.GetCount} $CPUCore
Apparently, there is a computer with 12 CPU Cores and the $CPUCore shows only 1 core. I think maybe there is a possibility that the $CPUCore only return the first digit, but how can I know it for sure?
Or, is there any other way to get the CPU Core numbers?
I don't know why the plugin is failing but you could ask Windows with something like this:
!include LogicLib.nsh
!ifndef ERROR_INSUFFICIENT_BUFFER
!define ERROR_INSUFFICIENT_BUFFER 122
!endif
!define RelationProcessorCore 0
!if "${NSIS_PTR_SIZE}" <= 4
Function GetProcessorPhysCoreCount
System::Store S
StrCpy $9 0 ; 0 if we fail
System::Call 'kernel32::GetLogicalProcessorInformationEx(i${RelationProcessorCore},i,*i0r2)i.r0?e'
Pop $3
${If} $3 = ${ERROR_INSUFFICIENT_BUFFER}
${AndIf} $2 <> 0
System::Alloc $2
System::Call 'kernel32::GetLogicalProcessorInformationEx(i${RelationProcessorCore},isr1,*ir2r2)i.r0'
Push $1
${If} $0 <> 0
loop_7:
IntOp $9 $9 + 1
System::Call *$1(i,i.r3)
IntOp $1 $1 + $3
IntOp $2 $2 - $3
IntCmp $2 0 "" loop_7 loop_7
${EndIf}
Pop $1
System::Free $1
${Else}
System::Call 'kernel32::GetLogicalProcessorInformation(i,*i0r2)i.r0?e'
Pop $3
${If} $3 = ${ERROR_INSUFFICIENT_BUFFER}
System::Alloc $2
System::Call 'kernel32::GetLogicalProcessorInformation(isr1,*ir2r2)i.r0'
Push $1
${If} $0 <> 0
loop_v:
System::Call *$1(i,i.r3)
${If} $3 = ${RelationProcessorCore}
IntOp $9 $9 + 1
${EndIf}
IntOp $1 $1 + 24
IntOp $2 $2 - 24
IntCmp $2 0 "" loop_v loop_v
${EndIf}
Pop $1
System::Free $1
${EndIf}
${EndIf}
Push $9
System::Store L
FunctionEnd
Function CountSetBits32
Exch $0
Push $1
Push $2
Push $3
StrCpy $3 0
StrCpy $1 0
loop:
IntOp $2 1 << $1
IntOp $2 $2 & $0
${IfThen} $2 <> 0 ${|} IntOp $3 $3 + 1 ${|}
IntOp $1 $1 + 1
StrCmp $1 32 "" loop
StrCpy $0 $3
Pop $3
Pop $2
Pop $1
Exch $0
FunctionEnd
Function GetProcessorLogicalCoreCount
System::Store S
StrCpy $9 0 ; 0 if we fail
System::Call 'kernel32::GetLogicalProcessorInformationEx(i${RelationProcessorCore},i,*i0r2)i.r0?e'
Pop $3
${If} $3 = ${ERROR_INSUFFICIENT_BUFFER}
${AndIf} $2 <> 0
System::Alloc $2
System::Call 'kernel32::GetLogicalProcessorInformationEx(i${RelationProcessorCore},isr1,*ir2r2)i.r0'
Push $1
${If} $0 <> 0
loop_7:
System::Call *$1(i,i.r3,&i22,&i2,i.r5)
Push $5
Call CountSetBits32
Pop $5
IntOp $9 $9 + $5
IntOp $1 $1 + $3
IntOp $2 $2 - $3
IntCmp $2 0 "" loop_7 loop_7
${EndIf}
Pop $1
System::Free $1
${Else}
System::Call 'kernel32::GetLogicalProcessorInformation(i,*i0r2)i.r0?e'
Pop $3
${If} $3 = ${ERROR_INSUFFICIENT_BUFFER}
System::Alloc $2
System::Call 'kernel32::GetLogicalProcessorInformation(isr1,*ir2r2)i.r0'
Push $1
${If} $0 <> 0
loop_v:
System::Call *$1(i,i.r3)
${If} $3 = ${RelationProcessorCore}
System::Call *$1(i.r3)
Push $3
Call CountSetBits32
Pop $3
IntOp $9 $9 + $3
${EndIf}
IntOp $1 $1 + 24
IntOp $2 $2 - 24
IntCmp $2 0 "" loop_v loop_v
${EndIf}
Pop $1
System::Free $1
${EndIf}
${EndIf}
Push $9
System::Store L
FunctionEnd
!endif
Section
Call GetProcessorPhysCoreCount
Pop $0
Call GetProcessorLogicalCoreCount
Pop $1
DetailPrint PhysCores=$0,LogicalCores=$1
SectionEnd
You should still use ${CPUFeatures.GetCount} as a fallback because GetLogicalProcessorInformation[Ex] does not exist on all versions of Windows...
I try to use the code from this forum and modified it a little bit so I only get the processor core numbers:
!include "LogicLib.nsh"
!define ERROR_INSUFFICIENT_BUFFER 122
; Size of SYSTEM_LOGICAL_PROCESSOR_INFORMATION on 32-bit systems
!define SYS_LOG_PROC_INFO_SIZE 24
; Offset of Relationship in the SYSTEM_LOGICAL_PROCESSOR_INFORMATION structure
!define RELATIONSHIP_OFFSET 4
; Enum value of Relationship identifying Processor Core
!define RELATIONPROCESSORCORE 0
; Count the number of bits set in given value
; Parameters: value
; Returns: number of bits set in given value
Function countbits
Exch $0
Push $1
Push $2
; Set initial value for number of bits set in $0
StrCpy $1 0
${While} $0 > 0
; Clear least significant bit set
IntOp $2 $0 - 1
IntOp $0 $0 & $2
; Increment number of bits set
IntOp $1 $1 + 1
${EndWhile}
; Return number of bits set
StrCpy $0 $1
Pop $2
Pop $1
Exch $0
FunctionEnd
; Evaluate processor information
; Paramaters: buffer, length
; Returns: number of cores
Function evalcpuinfo
Exch $0 ; length
Exch
Exch $1 ; buffer
Push $2
Push $3
Push $4
Push $5 ; Processor Cores
Push $6 ; Logical Processors
; Set buffer offset at the end of the buffer
StrCpy $2 $0
; Initialize number of Processor Cores and Logical Processors
StrCpy $5 0
StrCpy $6 0
; Iterate through buffer starting from end
${While} $2 >= ${SYS_LOG_PROC_INFO_SIZE}
; Calculate start address of an element
IntOp $2 $2 - ${SYS_LOG_PROC_INFO_SIZE}
IntOp $3 $1 + $2
; Get ProcessorMask value from element
System::Call "*$3(i.r4)"
Push $4
IntOp $3 $3 + ${RELATIONSHIP_OFFSET}
; Get Relationship value from element
System::Call "*$3(i.r4)"
${If} $4 == ${RELATIONPROCESSORCORE}
; Increment Processor cores
IntOp $5 $5 + 1
; Determine number of Logical Processor by counting the bits
; set in the value of ProcessorMask
Call countbits
Pop $4
; Sum up Logical Processors
IntOp $6 $6 + $4
${Else}
Pop $4
${EndIf}
${EndWhile}
; Set processor information as return value
StrCpy $0 $5
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Exch $0
FunctionEnd
; Get processor information
; Returns: number of Processor Cores and Logical Processors
Function getcpuinfo
Push $0
Push $1
Push $2
Push $3
Push $4
; GetLogicalProcessorInformation is only available on
; Windows XP SP3 or its successors.
; Initialize buffer and its length
StrCpy $1 0
StrCpy $2 0
; Determine required length of buffer
System::Call "kernel32::GetLogicalProcessorInformation(ir1, *ir2r2) i.r3 ? e"
Pop $4
${If} $3 == 0
${If} $4 == ${ERROR_INSUFFICIENT_BUFFER}
; Allocate buffer
System::Alloc $2
Pop $1
${If} $1 != 0
; Get processor information
System::Call "kernel32::GetLogicalProcessorInformation(ir1, *ir2r2) i.r3 ? e"
Pop $4
${If} $3 != 1
StrCpy $0 "Error: $4"
${Else}
Push $1 ; buffer
Push $2 ; length
Call evalcpuinfo
Pop $0
${EndIf}
; Deallocate buffer
System::Free $1
${Else}
StrCpy $0 "Error: memory allocation failed!"
${EndIf}
${Else}
StrCpy $0 "Error: $4"
${EndIf}
${Else}
StrCpy $0 "GetLogicalProcessorInformation is not available on your system!"
${EndIf}
Pop $4
Pop $3
Pop $2
Pop $1
Exch $0
FunctionEnd
I think the code is quite similar with Anders code. I have tried it in three different PCs and get the correct numbers for processor core.
My product has version w.x.y.z. My upgrade of that product has w.x.y.z+1. But the requirement is that before upgrade the fourth field of the version i.e; z should not be checked...i.e; myproduct should not take into consideration the z field.....Please do reply me with an answer.
You can read you App Version from registry and keep it as Variable, then trim it to specific number of chars. Check http://nsis.sourceforge.net/TrimText:_Trim_text_e.g._to_fit_in_a_label
Script below is will work for ANSI NSIS if you have such version installed if not, please change ReadRegStr accordingly.
Name "TrimTest"
OutFile "TrimTest.exe"
!include "TextFunc.nsh"
!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "WinMessages.nsh"
!include "WordFunc.nsh"
!insertmacro VersionCompare
Function TrimText
Exch $R0 ; char
Exch
Exch $R1 ; length
Exch 2
Exch $R2 ; text
Push $R3
Push $R4
StrLen $R3 $R2
IntCmp $R3 $R1 Done Done
StrCpy $R2 $R2 $R1
StrCpy $R3 0
IntOp $R3 $R3 + 1
StrCpy $R4 $R2 1 -$R3
StrCmp $R4 "" Done
StrCmp $R4 $R0 0 -3
IntOp $R3 $R3 + 1
StrCpy $R4 $R2 1 -$R3
StrCmp $R4 "" Done
StrCmp $R4 $R0 -3
IntOp $R3 $R3 - 1
StrCpy $R2 $R2 -$R3
StrCpy $R2 $R2
Done:
StrCpy $R0 $R2
Pop $R4
Pop $R3
Pop $R2
Pop $R1
Exch $R0 ; output
FunctionEnd
!macro TrimText Text Length Char Var
Push "${Text}"
Push "${Length}"
Push "${Char}"
Call TrimText
Pop "${Var}"
!macroend
!define TrimText "!insertmacro TrimText"
Section
ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\NSIS ANSI" "DisplayVersion"
${TrimText} $0 7 "-" $R0
;set max lenght to 7, where only 6 chars are needed, as trimmer will stop on - char
;in your case you will probably want to trim to first . from right side
;${TrimText} "string" "max_length" "up_to_char" "$out_var"
MessageBox MB_ICONEXCLAMATION "Line: $R0"
;Input $0 from registry should be "2.46.4-ANSI" if the latest ANSI NSIS is installed
;$R0 after trimm will be "Line: 2.46.4"
${VersionCompare} $R0 "2.46.6" $R2
${If} $R2 == 1 ;if 2.46.6 is lower that $R0
MessageBox MB_ICONEXCLAMATION "Your App Version is newer than this version of update"
${EndIf}
${If} $R2 == 0 ;versions are equal
MessageBox MB_ICONEXCLAMATION "Your App Version is equal to this version of update"
${EndIf}
${If} $R2 == 2 ;if 2.46.6 is higher that $R0
MessageBox MB_ICONEXCLAMATION "Your App Version is older and will be updated"
;here you can write your update section
${EndIf}
SectionEnd
In this case this can be a solution (based on http://nsis.sourceforge.net/Sort_String_2).
I have created Test Registry Key just for check with your {3.1.123 4.0.112 4.1.119}. Please replace with the one your application uses.
Name "TrimTest2"
OutFile "TrimTest2.exe"
!include "TextFunc.nsh"
!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "WinMessages.nsh"
!include "WordFunc.nsh"
!insertmacro VersionCompare
Function AdvStrSort
Exch $0 ; input string
Exch
Exch $1 ; count to get part
Exch
Exch 2
Exch $2 ; get ammount of chunks from end
Exch 2
Push $3
Push $4
Push $5
Push $6
Push $7
StrCpy $0 " $0"
StrCpy $3 0
StrCpy $4 0
loop:
IntOp $3 $3 - 1
StrCpy $6 $0 1 $3
StrCmp $6 "" skip
StrCmp $6 " " roger ; to change chunk seperators, edit this (" ")
Goto loop
roger:
StrCpy $7 $0 "" $3
IntOp $4 $4 + 1
StrCmp $4 $2 0 loop
StrCmp $1 $2 0 +3
StrCpy $0 $7
Goto end
skip:
StrCpy $4 0
StrLen $5 $7
top:
IntOp $4 $4 + 1
loop2:
IntOp $5 $5 - 1
StrCpy $3 $7 1 -$5
StrCmp $3 "" end
StrCmp $3 " " 0 loop2 ; to change chunk seperators, edit this too (" ")
StrCmp $4 $1 0 top
StrCpy $0 $7 -$5
end:
StrLen $1 $0
IntOp $1 $1 - 1
StrCpy $0 $0 $1 -$1
Pop $7
Pop $6
Pop $5
Pop $4
Pop $3
Pop $2
Pop $1
Exch $0 ; output string
FunctionEnd
Section
ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\UserApplicationName" "Version"
Push 1 ; get text after 1st chunk from string end
Push 1 ; get 1 chunk before the 1st
Push "$R1" ; input string (string that has spaces)
Call AdvStrSort
Pop $R0
MessageBox MB_ICONEXCLAMATION "Result: $R0"
${VersionCompare} $R0 "5.0.114" $R2
;MessageBox MB_ICONEXCLAMATION "Result: $R2"
${If} $R2 == 1 ;if 5.0.114 is lower that $R0
MessageBox MB_ICONEXCLAMATION "Your App Version is newer than this version of update"
${EndIf}
${If} $R2 == 0 ;versions are equal
MessageBox MB_ICONEXCLAMATION "Your App Version is equal to this version of update"
${EndIf}
${If} $R2 == 2 ;if 5.0.114 is higher that $R0
MessageBox MB_ICONEXCLAMATION "Your App Version is older and will be updated"
;here you can write your update section
${EndIf}
SectionEnd