NSIS roll-back installer if ExecWait command gets a specific return code - nsis

I'm currently using the following script to install drivers along with my application:
!macro customInstall
ExecWait '"$INSTDIR\resources\DPInst.exe" /sw'
!macroend
However, if DPInst returns >= 0x80010000, this means one or more of the driver installs has failed so I need to roll-back the installation and quit. Any idea how I would do this?

ExecWait can store the process exit code in the 2nd parameter. Not much you can do to roll it back, it is best just to do it early in the install phase:
!include LogicLib.nsh
Section
SetOutPath "$instdir\resources"
File "whatever\DPInst.exe"
ExecWait '"$INSTDIR\resources\DPInst.exe" /sw' $0
${If} $0 U>= 0x80010000
Delete "$INSTDIR\resources\DPInst.exe"
RMDir $instdir\resources
RMDir $instdir
MessageBox mb_iconstop "Error blah blah"
Abort
${EndIf}
SectionEnd

Related

NSIS nsi script error :- !insertmacro: macro named "SECTION_BEGIN" not found

In nsi script with MUI2.nsh
Code:-
!macro SECTION_BEGIN
Section ""
Call zip2exe.SetOutPath
!macroend
!macro SECTION_END
SectionEnd
!macroend
But If I want to define two or more section then in that case how to incorporate SECTION_BEGIN part?
Section "Main Component" MainCom
#SectionIn RO # Just means if in component mode this is locked
Call zip2exe.SetOutPath
;Store installation folder in registry
WriteRegStr HKLM "Software\${ZIP2EXE_NAME}" "" $INSTDIR
;Registry information for add/remove programs
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${ZIP2EXE_NAME}" "DisplayName" "${ZIP2EXE_NAME}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${ZIP2EXE_NAME}" "NoRepair" 1
;Create optional start menu shortcut for uninstaller and Main component
;Create uninstaller
WriteUninstaller "${ZIP2EXE_NAME}_uninstaller.exe"
#!macroend
#!macro SECTION_END
SectionEnd
#!macroend
;--------------------------------
;Uninstaller Section
Section "Uninstall"
;Delete the appdata directory + files
RMDir /r "${INSTDIR_DATA}\*.*"
RMDir "${INSTDIR_DATA}"
;Delete Start Menu Shortcuts
Delete "$SMPROGRAMS\${ZIP2EXE_NAME}\*.*"
RmDir "$SMPROGRAMS\${ZIP2EXE_NAME}"
SectionEnd
#!macro SECTION_END
If we omit SECTION_BEGIN part then error comes. If we mention SECTION_BEGIN in both sections then also error comes.
What will be the solution to this problem?
If actually want to use Zip2Exe then you can modify parts of NSIS\Contrib\zip2exe\Base.nsh from
!macro SECTION_END
SectionEnd
!macroend
to something like this
!macro SECTION_END
SectionEnd
!if /FileExists "c:\mycustomzip2exefiles\mycustomsections.nsh"
!include "c:\mycustomzip2exefiles\mycustomsections.nsh"
!endif
!macroend
You can then put whatever code you want in c:\mycustomzip2exefiles\mycustomsections.nsh:
Section "My other section"
SetOutPath $InstDir
File "anotherfile.txt"
SectionEnd
However, Zip2Exe is mainly something you use to create simple self-extracting executables, you should not use it to create full installers.
When you create a real installer you don't use Zip2Exe, you use MakeNSIS and there is no such thing as a SECTION_BEGIN macro, you just add as many sections as you want to your .NSI file.
Example2.nsi contains a basic installer/uninstaller.

Calling another program in nsis silent installation

I already have a nsis installer for my software. For supporting registration in silent installer (we have written complex logic in code rather than manipulating registry via nsis), i created an exe which accepts 2 parameters: RegName and RegKey for registration. Now I want to call this exe file with two parameters in silent installation and this call has to be optional and will depend if user has passed the two parameters.
So requirement boils down to :
-- Only process the key and registration name in silent installation
-- call my exe if the overall installation went successfull
OutFile "myinstaller.exe"
RequestExecutionLevel user
!include LogicLib.nsh
!include FileFunc.nsh
Function CheckRegistryParameters
${GetParameters} $0
${GetOptions} "$0" "/RegKey" $1
${GetOptions} "$0" "/RegName" $2
${If} $1 != ""
${AndIf} $2 != ""
WriteRegStr HKCU "Software\Test\$1" "Name" "$2"
Exec '"yourapplication.exe" "$1" "$2"'
${EndIf}
FunctionEnd
Section
${If} ${Silent}
Call CheckRegistryParameters
${EndIf}
SectionEnd
and run as myinstaller.exe /S /RegKey "Hello" /RegName "World"

Signing NSIS Uninstaller from Linux or Mac

I am porting our NSI installer to Linux and Mac instead of Windows to better integrate with our Maven build system.
We need to sign our installer and uninstaller. This was done as suggested at http://nsis.sourceforge.net/Signing_an_Uninstaller, but I just realized that it tries to run the tempinstaller to force it to produce the uninstaller.exe which can then be signed.
Obviously this trick doesn't work too well on *Nix systems and make this part of the process non-portable.
Does anyone has a better solution. I'm no expert at NSIS and wondering if there is a clever way to get the uninstall.exe so that it can be signed?
I don't think there is a real solution to this.
The installer and uninstaller uses the same exe code and only checks a flag (FH_FLAGS_UNINSTALL in firstheader) on startup to see if it is a uninstaller. Just flipping this bit is not enough though, the program would fail the CRC check and even if you bypass that the uninstaller data is compressed so you would have to decompress that to the correct location in the file. To actually accomplish this you would have to write a custom tool. You can see this operation in the NSIS source in exec.c if you search for EW_WRITEUNINSTALLER.
We need to sign our installer and uninstaller. This was done as suggested at http://nsis.sourceforge.net/Signing_an_Uninstaller, but I just realized that it tries to run the tempinstaller to force it to produce the uninstaller.exe which can then be signed. [...] this trick doesn't work too well on *Nix systems and make this part of the process non-portable.
If you exploit a stub installer for uninstall operations (no payload), this appears to be possible.
It will spawn an uninstall.exe process from the $TEMP folder, which is then capable of deleting $INSTDIR.
This script will create a stub (un)installer which can then be Authenticode Signed. It will compile on Windows, MacOS and Linux.
Caveats:
You'll have to manually bundle this into the installer (trivial)
You'll have to manage your own uninstall registry entries (trivial)
The look and feel may not match NSIS's default for uninstallers
You'll see the installer open twice (first from $INSTDIR, second from $TEMP). This is a child process which allows uninstall.exe to delete itself, similar to how NSIS does it in the Section "Uninstall".
You'll need a secondary .nsi script dedicated to uninstall operations, cumbersome if you have a lot of shared logic between your install/uninstall sections.
Worse, you'll have to AVOID the "Uninstall" section title, as you'll be placed into the same problem as the OP when that bytecode is generated.
When explicitly running from $TEMP some relative file logic will be incorrect. The example passes these back in as a $DELETE_DIR, $DELETE_EXE respectively.
The code:
!include MUI2.nsh
!include x64.nsh
!include LogicLib.nsh
!include FileFunc.nsh
!include WinMessages.nsh
!define MUI_PRODUCT "My App"
!define MUI_VERSION "1.0.0"
; Masquerade the title
!define MUI_PAGE_HEADER_TEXT "Uninstall My App"
!define MUI_PAGE_HEADER_SUBTEXT "Remove My App from your computer"
!define MUI_INSTFILESPAGE_FINISHHEADER_TEXT "Uninstallation Complete"
!define MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT "Uninstall was completed successfully."
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
!insertmacro GetParameters
RequestExecutionLevel admin
CRCCheck On
OutFile "uninstall.exe"
Name "Uninstall"
Var /GLOBAL RESPAWN
Var /GLOBAL DELETE_DIR
Var /GLOBAL DELETE_EXE
Section
; Masquerade as uninstall
SendMessage $HWNDPARENT ${WM_SETTEXT} 0 "STR:Uninstall"
${GetParameters} $0
${GetOptions} "$0" "/RESPAWN=" $RESPAWN
${GetOptions} "$0" "/DELETE_DIR=" $DELETE_DIR
${GetOptions} "$0" "/DELETE_EXE=" $DELETE_EXE
${If} $RESPAWN != ""
; We're running from $TEMP; Perform the uninstall
!define yay "We're running from $EXEPATH, yay, we can remove the install directory!$\n$\n"
!define myvars "$\tRESPAWN$\t$RESPAWN$\n$\tDELETE_EXE$\t$DELETE_EXE$\n$\tDELETE_DIR$\t$DELETE_DIR"
MessageBox MB_OK "${yay}${myvars}"
; Your uninstall code goes here
; RMDir /r $DELETE_DIR\*.*
; Delete "$DESKTOP\${MUI_PRODUCT}.lnk"
; Delete "$SMPROGRAMS\${MUI_PRODUCT}\*.*"
; RmDir "$SMPROGRAMS\${MUI_PRODUCT}"
; Delete Uninstaller And Unistall Registry Entries
; DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\${MUI_PRODUCT}"
; DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\${MUI_PRODUCT}"
; Remove the old version of ourself
ClearErrors
Delete $DELETE_EXE
IfErrors 0 +3
MessageBox MB_OK "File could NOT be deleted: $DELETE_EXE"
Goto +2
MessageBox MB_OK "File was successfully deleted: $DELETE_EXE"
; Remove ourself from $TEMP after reboot
Delete /REBOOTOK $EXEPATH
; ${If} ${RunningX64}
; ${EnableX64FSRedirection}
; ${EndIf}
SetDetailsPrint textonly
DetailPrint "Completed"
${Else}
; We're NOT running from $TEMP, copy to temp and respawn ourself
GetTempFileName $0
CopyFiles "$EXEPATH" "$0"
Exec '"$0" /RESPAWN=1 /DELETE_DIR="$EXEDIR" /DELETE_EXE="$EXEPATH"'
Quit
${EndIf}
SectionEnd
Function .onInit
; ${If} ${RunningX64}
; SetRegView 64
; ${DisableX64FSRedirection}
; ${EndIf}
FunctionEnd

Is it possible to conditionally add a file/folder to NSIS installer

Is it possible to conditionally add a file/folder and installation option to a NSIS installer?
My idea is that if the folder Foo exists at a given location it should be added to the installer and the option to install Foo should be added to the installer as well. But if the folder Foo does not exist, the NSIS script should just create the installer but leave Foo and the option to select Foo out of it.
You can try to include a file with /NONFATAL. If it exists, it will be included by the compiler. In runtime, you can check if installer was able to extract it.
File /NONFATAL "file.zip"
${If} ${FileExists} "$OUTDIR\file.zip"
...
${EndIf}
In NSIS 2 File /NONFATAL /R "c:\foo" is the best you can do without external tools and you need a little hack to hide the section when there are no files:
!include LogicLib.nsh
Page Components
Page InstFiles
Section "Main"
SetOutPath $InstDir
# File "C:\myfiles\myapp.exe"
SectionEnd
Section "Install Foo" SID_FOO
SetOutPath $InstDir
File /NONFATAL /r "C:\myfiles\foo\*.*"
SectionEnd
Function .onInit
SectionGetSize ${SID_FOO} $0
StrCmp $0 0 "" +3
SectionSetFlags ${SID_FOO} 0 ; Force all flags off including the checkmark
SectionSetText ${SID_FOO} "" ; Hide the section because its size is 0
FunctionEnd
If this is unacceptable you can use !system and get a little help from cmd.exe to check if something exists:
!tempfile INCEXIST
!system 'if exist "C:\myfiles\foo\*.*" echo !define HAVE_FOO > "${INCEXIST}"'
!include "${INCEXIST}"
!delfile "${INCEXIST}"
!ifdef HAVE_FOO
Section "Install Foo"
SetOutPath $InstDir
File /r "C:\myfiles\foo\*.*"
SectionEnd
!endif
In NSIS 3 !if supports a /FileExists switch:
!if /FileExists "C:\myfiles\foo\*.*"
Section "Install Foo"
SetOutPath $InstDir
File /r "C:\myfiles\foo\*.*"
SectionEnd
!endif
Example to replace file what depends on running service and exists or not at targget location
IfFileExists "$SYSDIR\my_file.dll" exist notexist
exist:
ExecWait 'net stop desired_service'
SetOutPath $SYSDIR
SetOverwrite on
File "/oname=$SYSDIR\my_file.dll" "Path to my file\my_file.dll"
ExecWait 'net start desired_service'
notexist:
.....what you want to do if doesn't exists

Having InstallDir within IF ELSE block

I try to have the following code from
; The default installation directory
InstallDir $PROGRAMFILES\${PRODUCT_NAME}
to
!include x64.nsh
${If} ${RunningX64}
; The default installation directory
InstallDir $PROGRAMFILES\${PRODUCT_NAME}
${Else}
; The default installation directory
InstallDir $PROGRAMFILES64\${PRODUCT_NAME}
${EndIf}
I get the following error :-
!insertmacro: _If
Error: Can't add entry, no section or function is open!
Error in macro _RunningX64 on macroline 2
Error in macro _If on macroline 9
Error in script "C:\Users\yccheok\Desktop\mysoftware.nsi" on line 17 -- aborting creation process
Is there way I can set the value for InstallDir, within if else block?
If you need a dynamic $InstDir you should not use InstallDir at all but set $InstDir in .onInit:
Installdir ""
!include LogicLib.nsh
!include x64.nsh
Function .onInit
${If} $InstDir == "" ; /D= was not used on the command line
${If} ${RunningX64}
StrCpy $InstDir "c:\foo"
${Else}
StrCpy $InstDir "c:\bar"
${EndIf}
${EndIf}
FunctionEnd
Your current if else block does not make any sense because you are selecting the 32 bit program files on x64 and the 64 bit program files on x86! It is OK to use $PROGRAMFILES64 on x86 so if you always want the "real" program files you can use $PROGRAMFILES64 for all platforms...

Resources