Calling another program in nsis silent installation - nsis

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"

Related

Installation is failing when installing the application using NSIS to create and control the service

My requirement is, when installing the application using NSIS the service should be installed and controlled (Start and Stop)
For this, first i downloaded "NSIS_Simple_Service_Plugin_1.30" and placed SimpleSC.dll in the "x86-ansi" directory.
I wrote the below code Under "Section"
;InstallServices:
SimpleSC::InstallService "testserv" "Test Service Testtwo" "16" "2" "E:\Source\Release\testserv.exe" "" "" ""
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +3
MessageBox MB_OK|MB_ICONSTOP "testserv installation failed: could not create service."
Abort
SimpleSC::SetServiceDescription "testserv" "Test Project service."
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
; We don't care about the result.
; Start a service using NSIS Simple Service Plugin
SimpleSC::StartService "testserv" ""
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +3
MessageBox MB_OK|MB_ICONSTOP "testserv installation failed: could not start service."
Abort
When i am testing the installer, it is showing the message "testserv installation failed: could not create service" from the MessageBox i kept.
Is it the correct place ("section") to write this code snippet or any other place like .OnInit?
And also while installing the service at the service name field, do we need to give "testserv" or "testserv.exe"
SimpleSc::InstallService "testserv"
or
SimpleSc::TestInstallService "testserv.exe"
which one is correct?
Below is the complete code:
; ServiceTest.nsi
;
;
; It will install ServiceTest.nsi into a directory that the user selects.
;--------------------------------
; The name of the installer in the path C:\Program Files\Registry
Name "ServiceTest"
; The file to write in the path E:\Source\NULLSOFT\src
OutFile "ServiceTest.exe"
; The default installation directory in the path C:\Program Files\ServiceTest
InstallDir $PROGRAMFILES\ServiceTest
; Registry key to check for directory (so if you install again, it will
; overwrite the old one automatically) It shows the path the path C:\Program Files\ServiceTest
InstallDirRegKey HKLM "Software\ServiceTest" "Install_Dir"
; Request application privileges for Windows Vista
; Below line is to check the administrative permissions
RequestExecutionLevel admin
; Below is the include file to check the conditions (If and else)
!include LogicLib.nsh
;--------------------------------
; Pages
Page components
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
;--------------------------------
;--------------------------------
;Installer Functions
Function .onInit
UserInfo::GetAccountType
pop $0
${If} $0 != "admin"
MessageBox mb_iconstop "Administrator rights required!"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
Quit
${Else}
MessageBox MB_OK "User is having Administrative Permissions"
${EndIf}
FunctionEnd
;--------------------------------
; The stuff to install
Section "ServiceTest (required)"
SectionIn RO
; Set output path to the installation directory. Here is the path C:\Program Files\ServiceTest
SetOutPath $INSTDIR
; Write the installation path into the registry
WriteRegStr HKLM SOFTWARE\ServiceTest "Install_Dir" "$INSTDIR"
WriteRegStr HKLM SOFTWARE\ServiceTest\Dialog "TestDlg" "0"
; Write the uninstall keys for Windows
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ServiceTest" "DisplayName" "ServiceTest"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ServiceTest" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ServiceTest" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ServiceTest" "NoRepair" 1
WriteUninstaller "uninstall.exe"
;InstallServices:
SimpleSC::InstallService "testserv" "Test Service Testtwo" "16" "2" "E:\Source\testserv\Release\testserv.exe" "" "" ""
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +3
MessageBox MB_OK|MB_ICONSTOP "testserv installation failed: could not create service."
Abort
SimpleSC::SetServiceDescription "testserv" "Test service."
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
; We don't care about the result.
; Start a service using NSIS Simple Service Plugin
SimpleSC::StartService "tetserv" ""
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +3
MessageBox MB_OK|MB_ICONSTOP "testserv installation failed: could not start service."
Abort
SectionEnd
; Optional section (can be disabled by the user)
Section "Start Menu Shortcuts"
CreateDirectory "$INSTDIR\ServiceTest"
CreateShortcut "$INSTDIR\ServiceTest\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
CreateShortcut "$INSTDIR\ServiceTest\ServiceTest (MakeNSISW).lnk" "$INSTDIR\ServiceTest.nsi" "" "$INSTDIR\ServiceTest.nsi" 0
SectionEnd
;--------------------------------
; Uninstaller
Section "Uninstall"
; Remove registry keys
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\ServiceTest"
DeleteRegKey HKLM SOFTWARE\ServiceTest
; Remove files and uninstaller
Delete $INSTDIR\ServiceTest.nsi
Delete $INSTDIR\uninstall.exe
; Remove shortcuts, if any
Delete "$INSTDIR\ServiceTest\*.*"
; Remove directories used
RMDir "$INSTDIR\ServiceTest"
RMDir "$INSTDIR"
; To Uninstall the Service
; Stop the service using NSIS Simple Service Plugin
SimpleSC::ExistsService "testserv"
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +1
; Stop the service using NSIS Simple Service Plugin
SimpleSC::StopService "tesserv"
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +3
MessageBox MB_OK|MB_ICONSTOP "testserv uninstallation failed: could not stop service."
Abort
; Stop the service using NSIS Simple Service Plugin
SimpleSC::RemoveService "testserv"
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
IntCmp $0 0 +3
MessageBox MB_OK|MB_ICONSTOP "testserv uninstallation failed: could not remove service."
Abort
SectionEnd
Is it the correct way to check "ExistsService", "StopService", and "RemoveService" in "Uninstall" section?
Please help me to resolve the issue and provide your thoughts to install and use the service.
The first parameter is the internal name of the service. It does not need a .exe suffix.
You problem is probably "E:\Code\PCPE\mainserv\Release\mainserv.exe" which is a path on your computer. It needs to be the path to the installed service on the end-users machine. It also helps if you include the error code in your message box so you can know for sure what the error actually is:
!include LogicLib.nsh
Section
SetOutPath $InstDir
File "E:\Code\PCPE\mainserv\Release\mainserv.exe"
SimpleSC::InstallService "mainserv" "UPS Service Testtwo" "16" "2" '"$InstDir\mainserv.exe"' "" "" ""
Pop $0
${If} $0 <> 0
MessageBox MB_ICONSTOP "InstallService failed, error $0"
Abort
${EndIf}
...
SectionEnd
The rest of your code looks OK but I would recommend that you use LogicLib.nsh instead of jumps with offset.

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

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

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

Making installer/reinstaller in NSIS

I need this routine - if RegKey with Installation Path is presented, we must to stop executed application (if executed), start Uninstaller silently (if uninstaller is not found - just delete all files in the directory) and set INSTDIR to value from Registry.
If RegKEy with Installation Path is not exists - start normal installation.
; Try to read Install Path from Reg
ReadRegStr $0 HKCU "Software\TelnetTray" "Path"
; Uninstall first if app installed
${If} $0 != ""
!define INSTDIR $0
; Try to stop app if executed
Stop "$INSTDIR\app.exe"
; Try to uninstall
${If} ${FIleExists} "$INSTDIR\uninst.exe"
ExecWait "$INSTDIR\uninst.exe /S"
; Or just delete all files in INSTDIR
${Else}
Delete "$INSTDIR\*"
${EndIf}
${EndIf}
You almost got it
; Try to read Install Path from Reg
ReadRegStr $0 HKCU "Software\TelnetTray" "Path"
; Uninstall first if app installed
${If} $0 != ""
StrCpy $INSTDIR $0
; Try to stop app if executed
${nsProcess::CloseProcess} "$INSTDIR\app.exe" $R0
; Try to uninstall
${If} ${FIleExists} "$INSTDIR\uninst.exe"
ExecWait "$INSTDIR\uninst.exe /S"
; Or just delete all files in INSTDIR
${Else}
RMDir /r "$INSTDIR"
${EndIf}
${EndIf}

NSIS uninstaller privileges according to installer

I have an NSIS installer for an application, which can be run as normal user. But if the user wants to install into the "Program Files" directory, it can still be accomplished by starting the installer with administrator privileges.
Now I have the problem, that the uninstaller is started with user privileges by default, even if the installation took place as administrator. This causes the uninstallation to silently fail. Even worse: It even states that the uninstall process was successful without being able to delete any files.
My question is: Is it possible to create an uninstaller during the installation, which requires (or better: requests itself with) the same privileges as the installation process?
You would have to implement this check yourself. You can check if you are admin in the installer with the UserInfo plugin and then store the result in a .ini, the registry or append the info to the uninstaller.exe:
InstallDir $temp\instdir
Section
UserInfo::GetAccountType
Pop $0
StrCmp $0 "Admin" 0 +2
StrCpy $0 1
IntOp $0 $0 & 1 ; $0 is now 1 if admin or 0 if not
SetOutPath $InstDir
WriteUninstaller "$InstDir\Uninstall.exe"
FileOpen $1 "$InstDir\Uninstall.exe" a
FileSeek $1 0 END
FileWriteByte $1 $0
FileClose $1
SectionEnd
Section Uninstall
FileOpen $1 "$ExePath" r
FileSeek $1 -1 END
FileReadByte $1 $0
FileClose $1
DetailPrint "Installer was admin: $0"
SectionEnd

Resources