Nsis Scripting to recognize previous installation directory - scripting

I am trying to recognized the previous installation directory in nsis scripting language using registry.
I found a entry for uninstollation in this registry
HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PaperTrlQBDConnector" "UninstallString"
but the registry value is \xxx\xxxx\PaperTrlQBDConnector\unistall.exe
i want to extract the path without uninstall.exe to a variable.
Function PreDirCheck
# discover if QBD Connector is already installed
ClearErrors
SetRegView 64
ClearErrors
;ReadRegStr $previous_install_dir HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PaperTrlQBDConnector" "PreDir"
;${if} $previous_install_dir == 0
ReadRegStr $previous_install_dir HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PaperTrlQBDConnector" "UninstallString"
;${Endif}
IfErrors done
;sectionsetflags ${sec1} 0
SetAutoClose false
strcpy $INSTDIR $previous_install_dir
MessageBox MB_ICONQUESTION|MB_YesNO|MB_DEFBUTTON2 "Detected previous version in Directory $previous_install_dir $\n Click Yes To update in the existing directory $\n Click No to completely remove PaperTrl SyncManager and install to a new location?" IDYes lbl_abort IDNo lbl_un
lbl_abort:
sectionsetflags ${sec2} 0
Abort ;skip page
lbl_un:
!insertmacro UninstallExisting
sectionsetflags ${sec2} 1
done:
SetAutoClose false
;!insertmacro MUI_PAGE_DIRECTORY
sectionsetflags ${sec2} 1
FunctionEnd
i tried this code but it gives the variable value with the unistall.exe for the path

InstallDirRegKey knows how to remove the .exe if you point it to your UninstallString value. Controlling its reg-view however is not possible.
You can also remove the last path component yourself:
!include "FileFunc.nsh"
Function .onInit
ReadRegStr $0 HK.. ...
${GetParent} "$0" $1
MessageBox MB_OK $1
FunctionEnd

Related

how to run a program on start up as an administrator using NSIS and NSSM?

I am using nsis to create installer for windows and I am using NSSM to run the application.
everything is fine when I install my server , but the problem my server needs to run as admin in order to use some functionalities. I solved my problem by manually going to "services" then Logon tab to enter my username and password.
However , I do not want to do that, I want the installer to ask the user to enter his admin credentials right after installing or before installing.
thanks in advance for your time and efforts
According to their documentation, NSSM already knows how to set service credentials so all you need is the custom page to tie it all together:
!include LogicLib.nsh
!include nsDialogs.nsh
!define MYSERVICE "FooBarBaz"
Var SvcUser
Var SvcPass
Page Directory
Page Custom MyServiceCredCreate MyServiceCredLeave ": Service credentials "
Page InstFiles
Function MyServiceCredCreate
nsDialogs::Create 1018
Pop $0
${IfThen} $0 == error ${|} Abort ${|}
${NSD_CreateLabel} 0 0 100% 12u "Username:"
Pop $0
${NSD_CreateText} 0 13u 100% 12u "$SvcUser"
Pop $1
${NSD_CreateLabel} 0 26u 100% 12u "Password:"
Pop $0
${NSD_CreatePassword} 0 39u 100% 12u "$SvcPass"
Pop $2
nsDialogs::Show
FunctionEnd
Function MyServiceCredLeave
${NSD_GetText} $1 $SvcUser
${NSD_GetText} $2 $SvcPass
${If} $SvcUser == ""
MessageBox MB_ICONSTOP "Must provide a username!"
Abort
${EndIf}
FunctionEnd
Section
SetOutPath $InstDir
File "myservicething.exe"
File "nssm.exe"
nsExec::Exec '"$InstDir\nssm.exe" install "${MYSERVICE}" "$InstDir\myservicething.exe"'
Pop $0
nsExec::Exec '"$InstDir\nssm.exe" set "${MYSERVICE}" ObjectName $SvcUser "$SvcPass"'
Pop $0
SectionEnd

How do I show an uninstaller window with CPack NSIS during re-installation?

I have the following CMakeLists.txt (the only thing in the directory):
cmake_minimum_required(VERSION 3.0.0)
project(CPackUninstallerTest)
set(CPACK_GENERATOR NSIS)
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS
"DetailPrint \\\"Sleeping...\\\"
Sleep 3000"
)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test.txt" "Some output\n")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/test.txt" TYPE DATA)
include(CPack)
Running cmake and cpack works fine, and .../build/CPackUninstallerTest-0.1.1-win64.exe is generated.
Running the installer works as expected:
And running the uninstaller (Uninstall.exe in the install directory) also works, where the sleep takes three seconds:
However, this uninstall window does not show up if I try to install on top of an existing installation. After clicking Yes here:
That window goes away for three seconds (as it uninstalls) before the new installer runs.
This is a terrible user experience and results in them re-running the installer while waiting for the hidden uninstaller, causing confusing results.
How do I configure NSIS or CMake/CPack to show the uninstaller progress bar when using CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL and re-installing?
Your issue could not be solved with NSIS interfaces of CMake.
I had a similar problem with CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL. The user could not install it again when uninstallation run manually.
The problem here is that manually running the uninstaller does not delete the install flag from windows registry keys.
My recommendation is that you can create your NSIS script by using NSIS template script of CMake. And you can add remove command easily to uninstaller section of new script.
And then you can give the new script as template to CMake.
As sad #hrn
For "normal" launch Uninstaller from installer (if apllication was installed)
You can create own NSIS.template.in (for example get cmake standart template and modify it)
Set new template as source
CMake checks if files NSIS.template.in, NSIS.InstallOptions.ini.in are in the module path
(module path in sample project was set with this command:
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/resources/nsis ${CMAKE_MODULE_PATH}))
If those files are found, then they are used for generating the installer.
There is an issue with sequential launch installer and uninstaller (not parallel)
NSIS ExecWait always waits for the child process but it does not wait for grandchildren.
So you need to use something more powerful,
for example nsis plugin StdUtils with functions StdUtils.ExecShellWaitEx/StdUtils.WaitForProcEx
There an issue with launching uninstaller:
firstly it copied itself to some temprorary directory and launches from this space,
and you dont know what you need to wait.
To avoid it launch uninstaller with flag:
${StdUtils.ExecShellWaitEx} $0 $1 "$uninstallerFullPath" "open" "_?=$uninstallerDirectoryPath"
If you cannot add plugin to NSIS directory (default C:\Program Files (x86)\NSIS),
you need to set path to this plugin from CMake,
add own variable CPACK_NSIS_INCLUDE_PLUGINS as script
and you can use it in some place in NSIS.template.in
CMakeLists:
set(CPACK_NSIS_INCLUDE_PLUGINS "
!addincludedir \\\"${PROJECT_SOURCE_DIR}\\\\resources\\\\nsis\\\\Include\\\"
!addplugindir /x86-unicode \\\"${PROJECT_SOURCE_DIR}\\\\resources\\\\nsis\\\\Plugins\\\\x86-unicode\\\"
!include 'StdUtils.nsh'
")
NSIS.template.in:
; on the begining of file
#CPACK_NSIS_OWN_INCLUDE_PLUGINS#
Function .onInit
...
Var /GLOBAL uninstallerDirectoryPath
Var /GLOBAL uninstallerFullPath
; Get the address of uninstaller
ReadRegStr $uninstallerFullPath HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\#CPACK_PACKAGE_INSTALL_REGISTRY_KEY#" "UninstallString"
; uninstallerFullPath has symbol " at the begin and symbol " at the end, we need to remove it
StrCpy $uninstallerFullPath $uninstallerFullPath "" 1 ; Remove first symbol
StrCpy $uninstallerFullPath $uninstallerFullPath -1 ; Remove last symbol
StrLen $2 "\#CPACK_NSIS_UNINSTALL_NAME#.exe"
StrCpy $uninstallerDirectoryPath $uninstallerFullPath -$2 # remove "\#CPACK_NSIS_UNINSTALL_NAME#.exe" from UninstallString to get path
StrCmp $uninstallerFullPath "" inst
MessageBox MB_YESNO|MB_ICONQUESTION \
"#CPACK_NSIS_PACKAGE_NAME# is already installed and must be removed before installation.$\n$\nDo you want to continue?" \
IDYES uninst IDNO 0
Quit
uninst:
${StdUtils.ExecShellWaitEx} $0 $1 "$uninstallerFullPath" "open" "_?=$uninstallerDirectoryPath"
; MessageBox MB_OK|MB_ICONSTOP "Result: $0 -> $1" ;returns "ok", "no_wait" or "error".
StrCmp $0 "error" ExecFailed ;check if process failed to create
StrCmp $0 "no_wait" WaitNotPossible ;check if process can be waited for - always check this!
StrCmp $0 "ok" WaitForProc ;make sure process was created successfully
MessageBox MB_OK|MB_ICONSTOP "error with StdUtils.ExecShellWaitEx"
Abort
ExecFailed:
MessageBox MB_OK|MB_ICONSTOP "Failed to create Uninstall process (error code: $1)"
Abort
WaitNotPossible:
MessageBox MB_OK|MB_ICONSTOP "Can not wait for Uninstall process."
Abort
WaitForProc:
${StdUtils.WaitForProcEx} $2 $1
IntCmp $2 0 uninst_successful
IntCmp $2 1 uninst_canceled
MessageBox MB_OK|MB_ICONSTOP "Uninstall process ends with error. (exit code: $2)"
Abort
uninst_canceled:
MessageBox MB_OK|MB_ICONINFORMATION "Uninstall canceled (exit code: $2)"
Quit
uninst_successful:
Delete "$uninstallerFullPath"
RMDir "$uninstallerDirectoryPath"
inst:

Application does not alway come to top after an install and start app

We have an installer system based on a WIX built MSI. The boot strapper is NSIS. It is just the way things went. And it all works fine now but for one little glitch.
There are two NSIS installers. One for new users. That runs the MSI conventionally so they contract screen can be agreed to. The the app checks for updates and the user can do just that. This is the second NSIS package for that:
Section -RA_unzip
InitPluginsDir
ReadRegStr $R1 HKCU "Software\Company\AppFolder\Property" "User Directory"
SetOutPath $R1
File "f:\cpp\AppName\deployment\AppName.msi"
ExecWait 'msiexec /i "$R1\AppName.msi" /L* msi.log /passive /norestart' $0
SectionEnd
Section -r_name_dlls
ReadRegStr $R0 HKCU "Software\Company\AppFolder\Property" "Program Directory"
Rename $R0\libssl_3.dll $R0\libssl-3.dll
Rename $R0\libcrypto_3.dll $R0\libcrypto-3.dll
SectionEnd
Section -Finishing Up
Sleep 1000
SectionEnd
Section -restartRA
ReadRegStr $R0 HKCU "Software\Company\AppFolder\Property" "Program Directory"
Exec $R0\ars.exe
Quit
SectionEnd
Less than half of the time the application comes up to the top of the Z-order. Sometimes it ends up at the bottom! This happens on my Windows7 and my wife's Windows10. If I run this without bumping the MSI version, (It just quits without an install), then the app window will always make it to the top.
I've even added BringWindowToTop(*GetMainWnd()); at the end of initialization when the main window is well established and running. And I did think it had something to do with the windows installer being slow to leave, that is why the Sleep 1000. It made no difference.
The only thing that is for sure is it happens when the windows installer actually does an install.
Calling Quit directly after Exec is not a good idea because if the installer quits before the child process has displayed its window the right to set the foreground window is lost.
You could try
ExecShell /WAITFORINPUTIDLE "" "$R0\ars.exe"
Sleep 1000
Quit

NSIS CreateShortcut With Environment Variables

Ok, I have a simplified version of my nsi script (see below). In the A2 section, I copy a single executable to the specified installation path, create environment variables, then do a SendMessage which is supposed to make all currently running processes aware of the env change. However, it seems that the NSIS installer process itself doesn't get updated because the shortcut I create in the Links section doesn't work
installer.nsi:
SetCompressor /FINAL zlib
!include LogicLib.nsh
!include WinMessages.nsh
!include x64.nsh
!define ENGINE "TEST"
!define DERIV "A2"
!define LIB_VER "v43"
!define RELEASE "v3dev2"
Name "${ENGINE}${DERIV} DTE ${RELEASE}"
OutFile "${ENGINE}${DERIV}-DTE.exe"
; display the installation directory page
; note that NSIS places the selected directory in $INSTDIR
; DirText ""
Page directory setDefaultInstallDirectory
Function setDefaultInstallDirectory
;check for an existing sim root and set it as
the default installation directory if it exists
ReadEnvStr $1 SIM_ROOT
${If} $1 != ""
StrCpy $INSTDIR $1
${EndIf}
FunctionEnd
; display the installation page and show a message
; when the installation is complete
Page instfiles "" "" finished
Function finished
MessageBox MB_OK "Installation Complete."
FunctionEnd
section "A2"
SetOutPath $INSTDIR\${ENGINE}\${DERIV}
File alt_control.exe
; Environment Variables
WriteRegStr HKCU "Environment" "SIM_ROOT" "$INSTDIR"
WriteRegStr HKCU "Environment" "ENGINE" "${ENGINE}"
WriteRegStr HKCU "Environment" "DERIV" "${DERIV}"
WriteRegExpandStr HKCU "Environment" "RUN_DIR" "%SIM_ROOT%\%ENGINE%\%DERIV%"
; Broadcast to all processes that they need to update their environment
; http://forums.winamp.com/showthread.php?t=118501
SendMessage ${HWND_BROADCAST}
${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
sectionEnd
section "Links"
; create the start menu directory & shortcuts
CreateDirectory $SMPROGRAMS\DTE
SetOutPath "$INSTDIR"
CreateShortCut "$SMPROGRAMS\DTE\AltControl.lnk"
"$INSTDIR\%ENGINE%\%DERIV%\alt_control.exe"
; Broadcast to all processes that they need to update their environment
; http://forums.winamp.com/showthread.php?t=118501
SendMessage ${HWND_BROADCAST}
${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
sectionEnd
After you run the installer, then attempt to run the start menu shortcut AltControl.lnk, you get a Windows missing shortcut error. Specifically: "Windows is searching for alt_control.exe. To locate the file yourself, click Browse."
If the environment variables already exist when you perform the installation, then the link works. Even weirder, if you click on the start menu shortcut properties and make some trivial change (like add and remove a space in the comment field) then click apply, Windows seems to regenerate the AltControl.lnk file (I know because the .lnk file increases in size even though no functional change was made through the dialog!?) and it works! So it seems like NSIS or some background Windows process in charge of generating/resolving .lnk files is not picking up on the newly created environment variables during the installation process. I've scoured the web and everything seems to indicate that the SendMessage I'm using should force everything to be aware of the newly created env variables. Thanks in advance to whoever can solve this mystery. You can use the included installer.nsi script to replicate my situation. Also note that it exhibits this behavior in multiple environments (XP, Vista, 7, with/without admin, etc.).
Most applications do not handle that message broadcast, it is mostly just for explorer.exe.
You can update the variables directly in your installer process and that will also be inherited by child processes:
System::Call 'Kernel32::SetEnvironmentVariable(t "ENGINE", t "v8")'

Registry with full permission

I am trying to give full permission for a registry in nsis but its not working. Below is a sample code.
WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\Demo\" "mydemo" "abc"
AccessControl::GrantOnRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Demo\" "PC116\Users" "FullAccess"
I tried doing it in a windows XP PC. I tried using REGINI using a vbscript which gives full permission for user "Everyone".
So can anyone help me on this.
The AccessControl plugin only supports the short HKEY names.
!include LogicLib.nsh
!define StringSID_BUILTIN_Users S-1-5-32-545
WriteRegStr HKLM "SOFTWARE\Demo\" "mydemo" "abc"
AccessControl::GrantOnRegKey HKLM "SOFTWARE\Demo\" "(${StringSID_BUILTIN_Users})" "FullAccess"
pop $0
${If} $0 != "ok"
pop $1
DetailPrint $0>$1 ; In your case: "error>Bad root key name (HKEY_LOCAL_MACHINE)"
${EndIf}