Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 119 additions & 83 deletions scripts/win/win_installer.nsi
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
!define ICON_FILE "rayforge.ico"
!endif

;--------------------------------
; Variables
Var PreviousInstallDir
Var MovedInstallDir

;--------------------------------
; General

Expand All @@ -41,33 +46,97 @@ Page instfiles
UninstPage uninstConfirm
UninstPage instfiles

;--------------------------------
; Running instance check
; The bundled executable name contains the version, so processes are
; matched on the product name instead of a fixed file name. The
; installer process itself shares that name prefix, so it is excluded
; from the match.

!macro CheckAppNotRunningBody LABEL
${LABEL}:
ExecWait `"$SYSDIR\cmd.exe" /C tasklist /FI "IMAGENAME eq ${PRODUCT_NAME}*" /FO CSV /NH | "$SYSDIR\find.exe" /I "${PRODUCT_NAME}" | "$SYSDIR\find.exe" /V /I "installer.exe" > nul` $0
${If} $0 = 0
MessageBox MB_RETRYCANCEL|MB_ICONEXCLAMATION "${PRODUCT_NAME} is still running.$\n$\nPlease close it and click Retry, or click Cancel to stop." /SD IDCANCEL IDRETRY ${LABEL}
Abort
${EndIf}
!macroend

Function CheckAppNotRunning
!insertmacro CheckAppNotRunningBody check_running
FunctionEnd

Function un.CheckAppNotRunning
!insertmacro CheckAppNotRunningBody un_check_running
FunctionEnd

;--------------------------------
; File type registration
; .png, .bmp, .jpeg, .jpg, .svg, .svgz and .dxf are shared with other
; applications, so Rayforge never claims their default association:
; it only registers its ProgIDs and lists them under the extension's
; OpenWithProgids subkey, which adds Rayforge to the "Open with" menu.
; Uninstalling therefore only removes registrations Rayforge owns and
; never needs to restore anything. The only case where a shared
; extension's default value is touched is when it still points at a
; Rayforge ProgID, which would dangle after the ProgID is deleted.

!macro RegisterSharedFileType EXT PROGID DESCRIPTION
WriteRegStr HKCR "${PROGID}" "" "${DESCRIPTION}"
WriteRegStr HKCR "${PROGID}\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "${PROGID}\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'
WriteRegStr HKCR ".${EXT}\OpenWithProgids" "${PROGID}" ""
!macroend

!macro UnregisterSharedFileType EXT PROGID
DeleteRegValue HKCR ".${EXT}\OpenWithProgids" "${PROGID}"
DeleteRegKey /ifempty HKCR ".${EXT}\OpenWithProgids"
ReadRegStr $R0 HKCR ".${EXT}" ""
${If} $R0 == "${PROGID}"
DeleteRegValue HKCR ".${EXT}" ""
${EndIf}
DeleteRegKey /ifempty HKCR ".${EXT}"
DeleteRegKey HKCR "${PROGID}"
!macroend

;--------------------------------
; Installer Section

Section "MainSection" SEC01
; Remove any previous installation first. The installer overwrites
; files in place, so a stale DLL from an older version that cannot be
; replaced (e.g. because the app is still running) leaves a mix of
; library versions behind, which crashes the app at startup with
; confusing DLL load errors.
StrCpy $R0 ""
; A running instance keeps files locked, so make sure it is closed
; before anything on disk is touched.
Call CheckAppNotRunning

; Move the previous installation aside instead of deleting it in
; place. A rename fails harmlessly while any file in it is still
; locked, and the intact copy stays available for restoration if
; writing the new files fails.
StrCpy $PreviousInstallDir ""
StrCpy $MovedInstallDir ""
ReadRegStr $R0 HKLM "Software\${PRODUCT_NAME}" "Install_Dir"
${If} $R0 != ""
RMDir /r "$R0"
${If} ${FileExists} "$R0.old"
RMDir /r "$R0.old"
${EndIf}
${If} ${FileExists} "$R0"
MessageBox MB_ICONSTOP "Could not remove the previous installation at$\n$R0$\n$\nPlease make sure Rayforge is not running, then run the installer again."
Abort
Rename "$R0" "$R0.old"
${If} ${FileExists} "$R0"
MessageBox MB_ICONSTOP "Could not move the previous installation at$\n$R0$\n$\nPlease make sure Rayforge is not running, then run the installer again."
Abort
${EndIf}
StrCpy $PreviousInstallDir "$R0"
StrCpy $MovedInstallDir "$R0.old"
${EndIf}
${EndIf}

SetOutPath "$INSTDIR"

; Copy all files from the PyInstaller output directory
File /r "..\..\dist\${APP_DIR_NAME}\*.*"

; Store installation folder
WriteRegStr HKLM "Software\${PRODUCT_NAME}" "Install_Dir" "$INSTDIR"

; Write the uninstaller
WriteUninstaller "$INSTDIR\uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_NAME}"
Expand All @@ -76,7 +145,7 @@ Section "MainSection" SEC01
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${APP_VERSION}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "NoRepair" 1

; Register .ryp file type
WriteRegStr HKCR ".ryp" "" "Rayforge.ProjectFile"
WriteRegStr HKCR "Rayforge.ProjectFile" "" "Rayforge Project File"
Expand All @@ -95,62 +164,37 @@ Section "MainSection" SEC01
WriteRegStr HKCR "Rayforge.RuidaFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.RuidaFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .png file type
WriteRegStr HKCR ".png" "" "Rayforge.PngFile"
WriteRegStr HKCR "Rayforge.PngFile" "" "PNG Image"
WriteRegStr HKCR "Rayforge.PngFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.PngFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .bmp file type
WriteRegStr HKCR ".bmp" "" "Rayforge.BmpFile"
WriteRegStr HKCR "Rayforge.BmpFile" "" "BMP Image"
WriteRegStr HKCR "Rayforge.BmpFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.BmpFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .jpeg file type
WriteRegStr HKCR ".jpeg" "" "Rayforge.JpegFile"
WriteRegStr HKCR "Rayforge.JpegFile" "" "JPEG Image"
WriteRegStr HKCR "Rayforge.JpegFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.JpegFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .jpg file type
WriteRegStr HKCR ".jpg" "" "Rayforge.JpgFile"
WriteRegStr HKCR "Rayforge.JpgFile" "" "JPEG Image"
WriteRegStr HKCR "Rayforge.JpgFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.JpgFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .svg file type
WriteRegStr HKCR ".svg" "" "Rayforge.SvgFile"
WriteRegStr HKCR "Rayforge.SvgFile" "" "SVG Image"
WriteRegStr HKCR "Rayforge.SvgFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.SvgFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .svgz file type
WriteRegStr HKCR ".svgz" "" "Rayforge.SvgzFile"
WriteRegStr HKCR "Rayforge.SvgzFile" "" "Compressed SVG Image"
WriteRegStr HKCR "Rayforge.SvgzFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.SvgzFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register .dxf file type
WriteRegStr HKCR ".dxf" "" "Rayforge.DxfFile"
WriteRegStr HKCR "Rayforge.DxfFile" "" "DXF Drawing"
WriteRegStr HKCR "Rayforge.DxfFile\DefaultIcon" "" "$INSTDIR\${EXECUTABLE_NAME},0"
WriteRegStr HKCR "Rayforge.DxfFile\shell\open\command" "" '"$INSTDIR\${EXECUTABLE_NAME}" "%1"'

; Register shared file types
!insertmacro RegisterSharedFileType "png" "Rayforge.PngFile" "PNG Image"
!insertmacro RegisterSharedFileType "bmp" "Rayforge.BmpFile" "BMP Image"
!insertmacro RegisterSharedFileType "jpeg" "Rayforge.JpegFile" "JPEG Image"
!insertmacro RegisterSharedFileType "jpg" "Rayforge.JpgFile" "JPEG Image"
!insertmacro RegisterSharedFileType "svg" "Rayforge.SvgFile" "SVG Image"
!insertmacro RegisterSharedFileType "svgz" "Rayforge.SvgzFile" "Compressed SVG Image"
!insertmacro RegisterSharedFileType "dxf" "Rayforge.DxfFile" "DXF Drawing"

; Create Start Menu shortcuts
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" "$INSTDIR\${EXECUTABLE_NAME}"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk" "$INSTDIR\uninstall.exe"

; The new installation is complete, so the moved-aside copy of the
; previous version is no longer needed.
${If} $MovedInstallDir != ""
RMDir /r "$MovedInstallDir"
${EndIf}
SectionEnd

;--------------------------------
; Uninstaller Section

Section "Uninstall"
Call un.CheckAppNotRunning

; Remove registry keys
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
DeleteRegKey HKLM "Software\${PRODUCT_NAME}"

; Unregister .ryp file type
DeleteRegKey HKCR ".ryp"
DeleteRegKey HKCR "Rayforge.ProjectFile"
Expand All @@ -163,33 +207,14 @@ Section "Uninstall"
DeleteRegKey HKCR ".rd"
DeleteRegKey HKCR "Rayforge.RuidaFile"

; Unregister .png file type
DeleteRegKey HKCR ".png"
DeleteRegKey HKCR "Rayforge.PngFile"

; Unregister .bmp file type
DeleteRegKey HKCR ".bmp"
DeleteRegKey HKCR "Rayforge.BmpFile"

; Unregister .jpeg file type
DeleteRegKey HKCR ".jpeg"
DeleteRegKey HKCR "Rayforge.JpegFile"

; Unregister .jpg file type
DeleteRegKey HKCR ".jpg"
DeleteRegKey HKCR "Rayforge.JpgFile"

; Unregister .svg file type
DeleteRegKey HKCR ".svg"
DeleteRegKey HKCR "Rayforge.SvgFile"

; Unregister .svgz file type
DeleteRegKey HKCR ".svgz"
DeleteRegKey HKCR "Rayforge.SvgzFile"

; Unregister .dxf file type
DeleteRegKey HKCR ".dxf"
DeleteRegKey HKCR "Rayforge.DxfFile"
; Unregister shared file types without touching their associations
!insertmacro UnregisterSharedFileType "png" "Rayforge.PngFile"
!insertmacro UnregisterSharedFileType "bmp" "Rayforge.BmpFile"
!insertmacro UnregisterSharedFileType "jpeg" "Rayforge.JpegFile"
!insertmacro UnregisterSharedFileType "jpg" "Rayforge.JpgFile"
!insertmacro UnregisterSharedFileType "svg" "Rayforge.SvgFile"
!insertmacro UnregisterSharedFileType "svgz" "Rayforge.SvgzFile"
!insertmacro UnregisterSharedFileType "dxf" "Rayforge.DxfFile"

; Remove the entire installation directory
; We delete the uninstaller first, then recursively remove its parent directory.
Expand All @@ -200,3 +225,14 @@ Section "Uninstall"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\*.*"
RMDir /r "$SMPROGRAMS\${PRODUCT_NAME}"
SectionEnd

;--------------------------------
; Put the previous installation back in place when an upgrade fails,
; so the user is not left with a partially deleted installation.

Function .onInstFailed
${If} $MovedInstallDir != ""
RMDir /r "$INSTDIR"
Rename "$MovedInstallDir" "$PreviousInstallDir"
${EndIf}
FunctionEnd
Loading