Files
2026-07-13 13:00:08 +08:00

214 lines
8.2 KiB
NSIS

Unicode true
####
## Reasonix per-user NSIS installer.
##
## This file is COMMITTED and customized (Wails leaves an existing project.nsi
## untouched and only regenerates wails_tools.nsh). The customizations vs.
## Wails' default template:
##
## 1. REQUEST_EXECUTION_LEVEL "user" + InstallDir under $LOCALAPPDATA - install
## without administrator rights. This is what lets the auto-updater re-run a
## freshly downloaded installer silently (`/S`) with no UAC prompt.
## 2. Uninstall registry under HKCU (not HKLM). Wails' wails.writeUninstaller /
## wails.deleteUninstaller macros hard-code HKLM, which a non-admin install
## cannot write - so we inline HKCU versions below instead.
## 3. InstallDir is remembered across updates via InstallDirRegKey +
## InstallLocation (HKCU\...\Uninstall\InstallLocation). When upgrading from
## a build that did not write InstallLocation yet, .onInit falls back to the
## old DisplayIcon path before using the default. Without this, every release
## forces the user back to %LOCALAPPDATA%\Programs\Reasonix even if they had
## moved the install to a different drive (e.g. D:\Tools\Reasonix); the silent
## auto-updater would re-run with /S into the wrong dir, leaving the old
## install orphaned.
##
## Everything else mirrors Wails' generated default. Defines below override the
## ProjectInfo values that wails_tools.nsh would otherwise populate.
####
## Install per-user (no admin). Must be defined BEFORE including wails_tools.nsh,
## which only sets the "admin" default when REQUEST_EXECUTION_LEVEL is undefined.
!define REQUEST_EXECUTION_LEVEL "user"
####
## Include the wails tools (auto-generated; provides INFO_* defines and the
## wails.* macros used below).
####
!include "wails_tools.nsh"
!include "FileFunc.nsh"
!include "LogicLib.nsh"
# The version information for this two must consist of 4 parts
VIProductVersion "${INFO_PRODUCTVERSION}.0"
VIFileVersion "${INFO_PRODUCTVERSION}.0"
VIAddVersionKey "CompanyName" "${INFO_COMPANYNAME}"
VIAddVersionKey "FileDescription" "${INFO_PRODUCTNAME} Installer"
VIAddVersionKey "ProductVersion" "${INFO_PRODUCTVERSION}"
VIAddVersionKey "FileVersion" "${INFO_PRODUCTVERSION}"
VIAddVersionKey "LegalCopyright" "${INFO_COPYRIGHT}"
VIAddVersionKey "ProductName" "${INFO_PRODUCTNAME}"
# Enable HiDPI support. https://nsis.sourceforge.io/Reference/ManifestDPIAware
ManifestDPIAware true
!include "MUI.nsh"
!define MUI_ICON "..\icon.ico"
!define MUI_UNICON "..\icon.ico"
# !define MUI_WELCOMEFINISHPAGE_BITMAP "resources\leftimage.bmp" #Include this to add a bitmap on the left side of the Welcome Page. Must be a size of 164x314
!define MUI_FINISHPAGE_NOAUTOCLOSE # Wait on the INSTFILES page so the user can take a look into the details of the installation steps
!define MUI_ABORTWARNING # This will warn the user if they exit from the installer.
!insertmacro MUI_PAGE_WELCOME # Welcome to the installer page.
# !insertmacro MUI_PAGE_LICENSE "resources\eula.txt" # Adds a EULA page to the installer
!insertmacro MUI_PAGE_DIRECTORY # In which folder install page.
!insertmacro MUI_PAGE_INSTFILES # Installing page.
!insertmacro MUI_PAGE_FINISH # Finished installation page.
!insertmacro MUI_UNPAGE_INSTFILES # Uinstalling page
!insertmacro MUI_LANGUAGE "English" # Set the Language of the installer
## The following two statements can be used to sign the installer and the uninstaller. The path to the binaries are provided in %1
#!uninstfinalize 'signtool --file "%1"'
#!finalize 'signtool --file "%1"'
Name "${INFO_PRODUCTNAME}"
OutFile "..\..\bin\${INFO_PROJECTNAME}-${ARCH}-installer.exe" # Name of the installer's file.
!define REASONIX_DEFAULT_INSTALLDIR "$LOCALAPPDATA\Programs\${INFO_PRODUCTNAME}"
!define REASONIX_UPDATE_HELPER "reasonix-update-helper.exe"
!define REASONIX_UNLOCK_RETRIES 60
InstallDirRegKey HKCU "${UNINST_KEY}" "InstallLocation" # Reuse the previous install path on update; .onInit falls back to the default on first install.
InstallDir "${REASONIX_DEFAULT_INSTALLDIR}" # Per-user install location (no admin rights required).
ShowInstDetails show # This will always show the installation details.
####
## Per-user uninstaller registry (HKCU). Replaces wails.writeUninstaller /
## wails.deleteUninstaller, which write HKLM and would fail without admin rights.
####
!macro reasonix.writeUninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
WriteRegStr HKCU "${UNINST_KEY}" "Publisher" "${INFO_COMPANYNAME}"
WriteRegStr HKCU "${UNINST_KEY}" "DisplayName" "${INFO_PRODUCTNAME}"
WriteRegStr HKCU "${UNINST_KEY}" "DisplayVersion" "${INFO_PRODUCTVERSION}"
WriteRegStr HKCU "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PRODUCT_EXECUTABLE}"
WriteRegStr HKCU "${UNINST_KEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKCU "${UNINST_KEY}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
# Persist the resolved install path so a subsequent update picks it up
# via InstallDirRegKey above. Without this, every release would force the
# user back to %LOCALAPPDATA%\Programs\Reasonix even if they had moved
# the install to a different drive (e.g. D:\Tools\Reasonix). The auto-
# updater re-runs this installer with /S and trusts the persisted path,
# so it has to be present before the silent re-install.
WriteRegStr HKCU "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
IntFmt $0 "0x%08X" $0
WriteRegDWORD HKCU "${UNINST_KEY}" "EstimatedSize" "$0"
!macroend
!macro reasonix.deleteUninstaller
Delete "$INSTDIR\uninstall.exe"
DeleteRegKey HKCU "${UNINST_KEY}"
!macroend
Function .onInit
!insertmacro wails.checkArchitecture
; InstallDirRegKey leaves $INSTDIR empty when the InstallLocation value is
; missing. Older installers still wrote DisplayIcon, so use its parent folder
; as a compatibility bridge before falling back to the per-user default.
StrCmp $INSTDIR "" 0 done
ClearErrors
ReadRegStr $0 HKCU "${UNINST_KEY}" "DisplayIcon"
IfErrors fallback
StrCmp $0 "" fallback
${GetParent} "$0" $INSTDIR
StrCmp $INSTDIR "" fallback done
fallback:
StrCpy $INSTDIR "${REASONIX_DEFAULT_INSTALLDIR}"
done:
FunctionEnd
Function reasonix.waitForExecutableUnlock
IfFileExists "$INSTDIR\${PRODUCT_EXECUTABLE}" 0 done
StrCpy $0 0
retry:
ClearErrors
FileOpen $1 "$INSTDIR\${PRODUCT_EXECUTABLE}" a
IfErrors locked
FileClose $1
Goto done
locked:
IntOp $0 $0 + 1
IntCmp $0 ${REASONIX_UNLOCK_RETRIES} failed 0 0
Sleep 1000
Goto retry
failed:
IfSilent silent interactive
interactive:
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "Reasonix is still running. Close Reasonix, then click Retry to continue the installation." IDRETRY retry IDCANCEL abort
Goto retry
silent:
SetErrorLevel 1618
abort:
Abort "Reasonix is still running. Close Reasonix and run the installer again."
done:
FunctionEnd
Section
!insertmacro wails.setShellContext
!insertmacro wails.webview2runtime
Call reasonix.waitForExecutableUnlock
SetOutPath $INSTDIR
!insertmacro wails.files
!if /FileExists "${REASONIX_UPDATE_HELPER}"
File "/oname=${REASONIX_UPDATE_HELPER}" "${REASONIX_UPDATE_HELPER}"
!else
!warning "${REASONIX_UPDATE_HELPER} was not found; Windows auto-update will fall back to installer-side waiting only."
!endif
CreateShortcut "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
CreateShortCut "$DESKTOP\${INFO_PRODUCTNAME}.lnk" "$INSTDIR\${PRODUCT_EXECUTABLE}"
!insertmacro wails.associateFiles
!insertmacro wails.associateCustomProtocols
!insertmacro reasonix.writeUninstaller
SectionEnd
Section "uninstall"
!insertmacro wails.setShellContext
RMDir /r "$AppData\${PRODUCT_EXECUTABLE}" # Remove the WebView2 DataPath
; Precision uninstall: delete main application files
Delete "$INSTDIR\${PRODUCT_EXECUTABLE}"
Delete "$INSTDIR\${REASONIX_UPDATE_HELPER}"
Delete "$SMPROGRAMS\${INFO_PRODUCTNAME}.lnk"
Delete "$DESKTOP\${INFO_PRODUCTNAME}.lnk"
!insertmacro wails.unassociateFiles
!insertmacro wails.unassociateCustomProtocols
!insertmacro reasonix.deleteUninstaller
; Only remove the installation directory if it is empty to prevent data loss
RMDir $INSTDIR
SectionEnd