Windows installer: an install made under the app's old name is older than any setup of the renamed app, whatever its version number (upgrade in place, data kept)

The renamed app may start its numbering again. Told apart by DisplayName in the uninstall key
(the old app wrote its old name there; this template writes the new one). Also exempt from the
silent-downgrade abort. Compiled with and without a legacy name and run on the test variant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
CRTkafa 2026-09-30 23:23:43 +07:00
parent 16957f4ea7
commit f8144a3bff
2 changed files with 34 additions and 3 deletions

View file

@ -492,7 +492,9 @@ The npm package adds the app-side tooling:
install the updater has moved on. `/FORCE` reinstalls the same version (repair), `/NORUN` does install the updater has moved on. `/FORCE` reinstalls the same version (repair), `/NORUN` does
not open the app, `/UPDATE` is the updater's path and always installs. An app renamed after its not open the app, `/UPDATE` is the updater's path and always installs. An app renamed after its
first release keeps its registry keys by defining `WAYBETTER_LEGACY_NAME` in its installer hooks first release keeps its registry keys by defining `WAYBETTER_LEGACY_NAME` in its installer hooks
(`bundle.windows.nsis.installerHooks`). (`bundle.windows.nsis.installerHooks`). An install made under the old name (its `DisplayName` is
still that name) counts as older than any setup of the renamed app, whatever its version number:
the renamed app may start its numbering again and still upgrades such an install in place.
## Consuming it ## Consuming it

View file

@ -7,7 +7,9 @@
; - the same or a newer version is installed: installs nothing, opens the installed app (its ; - the same or a newer version is installed: installs nothing, opens the installed app (its
; single-instance guard shows the running window) and exits 0. So an old setup left in ; single-instance guard shows the running window) and exits 0. So an old setup left in
; Downloads can never replace, restart or downgrade an install the updater has moved on; ; Downloads can never replace, restart or downgrade an install the updater has moved on;
; - an older version is installed: upgrades in place (app data untouched), then opens the app. ; - an older version is installed: upgrades in place (app data untouched), then opens the app;
; - an install made under the app's old name (see WAYBETTER_LEGACY_NAME below) counts as older,
; whatever its version number says: it is upgraded in place, data kept.
; Flags: `/NORUN` never opens the app, `/FORCE` reinstalls the same version (repair; it still never ; Flags: `/NORUN` never opens the app, `/FORCE` reinstalls the same version (repair; it still never
; downgrades), `/UPDATE` is the updater's path (tauri-plugin-updater: `/UPDATE /R /ARGS ...`), ; downgrades), `/UPDATE` is the updater's path (tauri-plugin-updater: `/UPDATE /R /ARGS ...`),
; which always installs and is otherwise Tauri's. `/NS` (no shortcuts) and `/D=<folder>` as in Tauri. ; which always installs and is otherwise Tauri's. `/NS` (no shortcuts) and `/D=<folder>` as in Tauri.
@ -17,6 +19,9 @@
; (`bundle.windows.nsis.installerHooks`), which are included before anything here reads it: ; (`bundle.windows.nsis.installerHooks`), which are included before anything here reads it:
; !define WAYBETTER_LEGACY_NAME "Old Product Name" ; !define WAYBETTER_LEGACY_NAME "Old Product Name"
; !define WAYBETTER_LEGACY_MANUFACTURER "Old Publisher" ; optional, defaults to the old name ; !define WAYBETTER_LEGACY_MANUFACTURER "Old Publisher" ; optional, defaults to the old name
; The renamed app may start its version numbers again (a 1.0.0 after an old 1.0.2): an install
; whose DisplayName is still the old name was written by the old app, and every setup of the
; renamed app is newer than it.
Unicode true Unicode true
ManifestDPIAware true ManifestDPIAware true
; wayBetter: never show a wizard. ; wayBetter: never show a wizard.
@ -111,6 +116,7 @@ Var ForceMode
Var NoShortcutMode Var NoShortcutMode
Var WixMode Var WixMode
Var OldMainBinaryName Var OldMainBinaryName
Var LegacyInstall
Name "${PRODUCTNAME}" Name "${PRODUCTNAME}"
BrandingText "${COPYRIGHT}" BrandingText "${COPYRIGHT}"
@ -330,10 +336,24 @@ Function .onInit
${EndIf} ${EndIf}
FunctionEnd FunctionEnd
; wayBetter: $LegacyInstall = 1 when the install recorded in ${UNINSTKEY} was made by the app under
; its old name (its DisplayName is still ${LEGACYNAME}; this template writes ${PRODUCTNAME}), else 0.
; Such an install is older than this setup whatever its version number says.
!macro WaybetterLegacyInstall
StrCpy $LegacyInstall 0
!ifdef LEGACYNAME
ReadRegStr $R6 SHCTX "${UNINSTKEY}" "DisplayName"
${If} $R6 == "${LEGACYNAME}"
StrCpy $LegacyInstall 1
${EndIf}
!endif
!macroend
; wayBetter: when the app is installed in $INSTDIR at this setup's version or a newer one, open it ; wayBetter: when the app is installed in $INSTDIR at this setup's version or a newer one, open it
; and quit with exit code 0: nothing is copied, no running app is closed, nothing is downgraded. ; and quit with exit code 0: nothing is copied, no running app is closed, nothing is downgraded.
; Returns (and the install goes ahead) when nothing is installed, when the registry names an install ; Returns (and the install goes ahead) when nothing is installed, when the registry names an install
; whose exe is gone, when the installed version is older, or on /FORCE with the same version. ; whose exe is gone, when the installed version is older, when the install was made under the app's
; old name, or on /FORCE with the same version.
Function OpenInstalledIfCurrent Function OpenInstalledIfCurrent
; $R1 the installed exe's name, $R2 the installed version, $R3 this setup compared to it ; $R1 the installed exe's name, $R2 the installed version, $R3 this setup compared to it
ReadRegStr $R1 SHCTX "${UNINSTKEY}" "MainBinaryName" ReadRegStr $R1 SHCTX "${UNINSTKEY}" "MainBinaryName"
@ -344,6 +364,12 @@ Function OpenInstalledIfCurrent
Return Return
${EndIf} ${EndIf}
; Installed under the old name: older, whatever its number (upgrade in place).
!insertmacro WaybetterLegacyInstall
${If} $LegacyInstall = 1
Return
${EndIf}
ReadRegStr $R2 SHCTX "${UNINSTKEY}" "DisplayVersion" ReadRegStr $R2 SHCTX "${UNINSTKEY}" "DisplayVersion"
${If} $R2 == "" ${If} $R2 == ""
; No version in the registry: the exe's own file version, a.b.c.d -> a.b.c ; No version in the registry: the exe's own file version, a.b.c.d -> a.b.c
@ -389,7 +415,10 @@ Section EarlyChecks
; If downgrading (wayBetter: there is no reinstall page to compare the versions, so here) ; If downgrading (wayBetter: there is no reinstall page to compare the versions, so here)
StrCpy $R0 0 StrCpy $R0 0
ReadRegStr $R2 SHCTX "${UNINSTKEY}" "DisplayVersion" ReadRegStr $R2 SHCTX "${UNINSTKEY}" "DisplayVersion"
; (An install made under the app's old name is never a downgrade target: it is older.)
!insertmacro WaybetterLegacyInstall
${If} $R2 != "" ${If} $R2 != ""
${AndIf} $LegacyInstall <> 1
nsis_tauri_utils::SemverCompare "${VERSION}" $R2 nsis_tauri_utils::SemverCompare "${VERSION}" $R2
Pop $R0 Pop $R0
${EndIf} ${EndIf}