Skip to content

Commit

Permalink
fix(nsis): per machine boring installer
Browse files Browse the repository at this point in the history
Closes #564
  • Loading branch information
develar committed Jul 11, 2016
1 parent 9f52848 commit 1bd32a7
Show file tree
Hide file tree
Showing 7 changed files with 317 additions and 396 deletions.
2 changes: 1 addition & 1 deletion docs/Options.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ See [NSIS target notes](https://github.com/electron-userland/electron-builder/wi
| --- | ---
| oneClick | <a name="NsisOptions-oneClick"></a>One-click installation. Defaults to `true`.
| perMachine | <a name="NsisOptions-perMachine"></a>Install per all users (per-machine). Defaults to `false`.
| allowElevation | <a name="NsisOptions-allowElevation"></a>Allow requesting for elevation. If false, user will have to restart installer with elevated permissions. Defaults to `true`.
| allowElevation | <a name="NsisOptions-allowElevation"></a>*boring installer only.* Allow requesting for elevation. If false, user will have to restart installer with elevated permissions. Defaults to `true`.
| installerHeader | <a name="NsisOptions-installerHeader"></a>*boring installer only.* `MUI_HEADERIMAGE`, relative to the project directory. Defaults to `build/installerHeader.bmp`
| installerHeaderIcon | <a name="NsisOptions-installerHeaderIcon"></a>*one-click installer only.* The path to header icon (above the progress bar), relative to the project directory. Defaults to `build/installerHeaderIcon.ico` or application icon.

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"typescript": "2.0.0-dev.20160705",
"update-notifier": "^1.0.2",
"uuid-1345": "^0.99.6",
"yargs": "^4.7.1"
"yargs": "^4.8.0"
},
"optionalDependencies": {
"appdmg": "^0.4.5"
Expand Down
432 changes: 76 additions & 356 deletions templates/nsis/NsisMultiUser.nsh

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions templates/nsis/boring-installer.nsh
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
# http://nsis.sourceforge.net/Run_an_application_shortcut_after_an_install
!include NsisMultiUser.nsh
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION "StartApp"

!define MUI_CUSTOMFUNCTION_GUIINIT GuiInit

!insertmacro MULTIUSER_PAGE_INSTALLMODE
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH

!insertmacro MUI_LANGUAGE "English"

# uninstall pages
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_UNPAGE_INSTFILES

Function GuiInit
!insertmacro UAC_PageElevation_OnGuiInit
FunctionEnd
27 changes: 21 additions & 6 deletions templates/nsis/common.nsh
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
!include x64.nsh

BrandingText "${PRODUCT_NAME} ${VERSION}"
ShowInstDetails nevershow
ShowUninstDetails nevershow
FileBufSize 64
Name "${PRODUCT_NAME}"
Unicode true

!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "UninstallString"
!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME "InstallLocation"

!define MULTIUSER_INSTALLMODE_INSTDIR "${APP_GUID}"
!define MULTIUSER_INSTALLMODE_INSTALL_REGISTRY_KEY "${APP_GUID}"
!define MULTIUSER_INSTALLMODE_UNINSTALL_REGISTRY_KEY "${APP_GUID}"

!define APP_EXECUTABLE_FILENAME "${PRODUCT_FILENAME}.exe"
!define UNINSTALL_FILENAME "Uninstall ${PRODUCT_FILENAME}.exe"
!define UNINSTALL_FILENAME "Uninstall ${PRODUCT_FILENAME}.exe"

!macro check64BitAndSetRegView
!ifdef APP_64
${IfNot} ${AtLeastWin7}
MessageBox MB_OK "Windows 7 and above is required"
Quit
${EndIf}

${If} ${RunningX64}
SetRegView 64
${Else}
!ifndef APP_32
MessageBox MB_OK|MB_ICONEXCLAMATION "64-bit Windows is required"
Quit
!endif
${EndIf}
!endif
!macroend
53 changes: 23 additions & 30 deletions templates/nsis/installer.nsi
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
!include "common.nsh"
!include "MUI2.nsh"
!include "NsisMultiUser.nsh"
!include "multiUser.nsh"
!include "nsProcess.nsh"
!include "allowOnlyOneInstallerInstace.nsh"
!include "checkAppRunning.nsh"
!include x64.nsh
!include WinVer.nsh

Function StartApp
ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
FunctionEnd

!ifdef ONE_CLICK
Function StartApp
!ifdef INSTALL_MODE_PER_ALL_USERS
!include UAC.nsh
!insertmacro UAC_AsUser_ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk" "" "" ""
!else
ExecShell "" "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
!endif
FunctionEnd

SilentUnInstall silent
AutoCloseWindow true
!insertmacro MUI_PAGE_INSTFILES
Expand All @@ -23,35 +27,26 @@ FunctionEnd
RequestExecutionLevel user
!endif
!else
RequestExecutionLevel user
!include "boring-installer.nsh"
!endif

Var startMenuLink
Var desktopLink

Function .onInit
${IfNot} ${AtLeastWin7}
MessageBox MB_OK "Windows 7 and above is required"
Quit
${EndIf}
!insertmacro check64BitAndSetRegView
!insertmacro initMultiUser "" ""

!insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
!insertmacro MULTIUSER_INIT
!ifdef ONE_CLICK
!insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
!else
${IfNot} ${UAC_IsInnerInstance}
!insertmacro ALLOW_ONLY_ONE_INSTALLER_INSTACE
${EndIf}
!endif

InitPluginsDir

${If} ${RunningX64}
!ifdef APP_64
SetRegView 64
!endif
${Else}
!ifndef APP_32
MessageBox MB_OK|MB_ICONEXCLAMATION "64-bit Windows is required"
Quit
!endif
${EndIf}

SetCompress off
!ifdef APP_32
File /oname=$PLUGINSDIR\app-32.7z "${APP_32}"
Expand All @@ -67,7 +62,8 @@ Function .onInit
FunctionEnd

Function un.onInit
!insertmacro MULTIUSER_UNINIT
!insertmacro check64BitAndSetRegView
!insertmacro initMultiUser Un un.
FunctionEnd

Section "install"
Expand Down Expand Up @@ -123,6 +119,8 @@ Section "install"
SectionEnd

Section "un.install"
SetAutoClose true

!insertmacro CHECK_APP_RUNNING "uninstall"

StrCpy $startMenuLink "$SMPROGRAMS\${PRODUCT_FILENAME}.lnk"
Expand All @@ -141,9 +139,4 @@ Section "un.install"
RMDir /r "$APPDATA\${PRODUCT_FILENAME}"

!insertmacro MULTIUSER_RegistryRemoveInstallInfo

!ifdef ONE_CLICK
# strange, AutoCloseWindow true doesn't work for uninstaller, so, just quit
Quit
!endif
SectionEnd
187 changes: 187 additions & 0 deletions templates/nsis/multiUser.nsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
!include FileFunc.nsh

!define FOLDERID_UserProgramFiles {5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}
!define KF_FLAG_CREATE 0x00008000

!ifdef UNINSTALL_FILENAME & VERSION & APP_EXECUTABLE_FILENAME & PRODUCT_NAME & COMPANY_NAME & PRODUCT_FILENAME
!else
!error "Should define all variables: UNINSTALL_FILENAME & VERSION & APP_EXECUTABLE_FILENAME & PRODUCT_NAME & COMPANY_NAME & PRODUCT_FILENAME"
!endif

!define INSTALL_REGISTRY_KEY "Software\${APP_GUID}"
!define UNINSTALL_REGISTRY_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_GUID}"

!ifndef MULTIUSER_INSTALLMODE_DISPLAYNAME
!define MULTIUSER_INSTALLMODE_DISPLAYNAME "${PRODUCT_NAME} ${VERSION}"
!endif

# Current Install Mode ("AllUsers" or "CurrentUser")
Var MultiUser.InstallMode
Var HasPerUserInstallation ; 0 (false) or 1 (true)
Var HasPerMachineInstallation ; 0 (false) or 1 (true)
Var PerUserInstallationFolder
Var PerMachineInstallationFolder

# Sets install mode to "per-machine" (all users).
!macro MULTIUSER_INSTALLMODE_ALLUSERS UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
# Install mode initialization - per-machine
StrCpy $MultiUser.InstallMode AllUsers

SetShellVarContext all

!if "${UNINSTALLER_PREFIX}" != UN
;Set default installation location for installer
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCT_FILENAME}"
!endif

; Checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
ReadRegStr $PerMachineInstallationFolder HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation
${if} $PerMachineInstallationFolder != ""
StrCpy $INSTDIR $PerMachineInstallationFolder
${endif}

!ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION
Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}"
!endif
!macroend

# Sets install mode to "per-user".
!macro MULTIUSER_INSTALLMODE_CURRENTUSER UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
StrCpy $MultiUser.InstallMode CurrentUser

SetShellVarContext current

!if "${UNINSTALLER_PREFIX}" != UN
# http://www.mathiaswestin.net/2012/09/how-to-make-per-user-installation-with.html
StrCpy $0 "$LocalAppData\Programs"
# Win7 has a per-user programfiles known folder and this can be a non-default location
System::Call 'Shell32::SHGetKnownFolderPath(g "${FOLDERID_UserProgramFiles}",i ${KF_FLAG_CREATE},i0,*i.r2)i.r1'
${If} $1 == 0
System::Call '*$2(&w${NSIS_MAX_STRLEN} .r1)'
StrCpy $0 $1
System::Call 'Ole32::CoTaskMemFree(ir2)'
${EndIf}
StrCpy $Instdir "$0\${PRODUCT_FILENAME}"
!endif

# Checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
ReadRegStr $PerUserInstallationFolder HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation
${if} $PerUserInstallationFolder != ""
StrCpy $INSTDIR $PerUserInstallationFolder
${endif}

!ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION
Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}"
!endif
!macroend

Function MultiUser.InstallMode.AllUsers
!insertmacro MULTIUSER_INSTALLMODE_ALLUSERS "" ""
FunctionEnd

Function MultiUser.InstallMode.CurrentUser
!insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER "" ""
FunctionEnd

Function un.MultiUser.InstallMode.AllUsers
!insertmacro MULTIUSER_INSTALLMODE_ALLUSERS UN un.
FunctionEnd

Function un.MultiUser.InstallMode.CurrentUser
!insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER UN un.
FunctionEnd

!macro MULTIUSER_INIT_TEXTS
!ifndef MULTIUSER_INIT_TEXT_ADMINREQUIRED
!define MULTIUSER_INIT_TEXT_ADMINREQUIRED "$(^Caption) requires administrator privileges."
!endif

!ifndef MULTIUSER_INIT_TEXT_POWERREQUIRED
!define MULTIUSER_INIT_TEXT_POWERREQUIRED "$(^Caption) requires at least Power User privileges."
!endif

!ifndef MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE
!define MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE "Your user account does not have sufficient privileges to install $(^Name) for all users of this computer."
!endif
!macroend

!macro initMultiUser UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX
!ifdef ONE_CLICK
!ifdef INSTALL_MODE_PER_ALL_USERS
Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
!else
Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
!endif
!else
!insertmacro UAC_PageElevation_OnInit

${If} ${UAC_IsInnerInstance}
${AndIfNot} ${UAC_IsAdmin}
# special return value for outer instance so it knows we did not have admin rights
SetErrorLevel 0x666666
Quit
${EndIf}

!insertmacro MULTIUSER_INIT_TEXTS

# checks registry for previous installation path (both for upgrading, reinstall, or uninstall)
StrCpy $HasPerMachineInstallation "0"
StrCpy $HasPerUserInstallation "0"

# set installation mode to setting from a previous installation
ReadRegStr $PerMachineInstallationFolder HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation
${if} $PerMachineInstallationFolder != ""
StrCpy $HasPerMachineInstallation "1"
${endif}

ReadRegStr $PerUserInstallationFolder HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation
${if} $PerUserInstallationFolder != ""
StrCpy $HasPerUserInstallation "1"
${endif}

${if} $HasPerUserInstallation == "1"
${andif} $HasPerMachineInstallation == "0"
Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
${elseif} $HasPerUserInstallation == "0"
${andif} $HasPerMachineInstallation == "1"
Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
${else}
# if there is no installation, or there is both per-user and per-machine
!ifdef INSTALL_MODE_PER_ALL_USERS
Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
!else
Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
!endif
${endif}
!endif
!macroend

# SHCTX is the hive HKLM if SetShellVarContext all, or HKCU if SetShellVarContext user
!macro MULTIUSER_RegistryAddInstallInfo
# Write the installation path into the registry
WriteRegStr SHCTX "${INSTALL_REGISTRY_KEY}" InstallLocation "$INSTDIR"

# Write the uninstall keys for Windows
${if} $MultiUser.InstallMode == "AllUsers"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" DisplayName "${MULTIUSER_INSTALLMODE_DISPLAYNAME}"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" UninstallString '"$INSTDIR\${UNINSTALL_FILENAME}" /allusers'
${else}
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" DisplayName "${MULTIUSER_INSTALLMODE_DISPLAYNAME} (only current user)"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" UninstallString '"$INSTDIR\${UNINSTALL_FILENAME}" /currentuser'
${endif}

WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" "DisplayVersion" "${VERSION}"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" "DisplayIcon" "$INSTDIR\${APP_EXECUTABLE_FILENAME},0"
WriteRegStr SHCTX "${UNINSTALL_REGISTRY_KEY}" "Publisher" "${COMPANY_NAME}"
WriteRegDWORD SHCTX "${UNINSTALL_REGISTRY_KEY}" NoModify 1
WriteRegDWORD SHCTX "${UNINSTALL_REGISTRY_KEY}" NoRepair 1

${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD SHCTX "${UNINSTALL_REGISTRY_KEY}" "EstimatedSize" "$0"
!macroend

!macro MULTIUSER_RegistryRemoveInstallInfo
DeleteRegKey SHCTX "${UNINSTALL_REGISTRY_KEY}"
DeleteRegKey SHCTX "${INSTALL_REGISTRY_KEY}"
!macroend

0 comments on commit 1bd32a7

Please sign in to comment.