mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-25 21:22:20 +08:00
* feat(service): hand upgraded cores to the service through the installer The service now only executes administrator-staged core copies, so an in-app core upgrade has to deliver the new bytes there before the restart asks for them; otherwise a service-mode restart keeps running the previous core while the UI reports the new version. After publishing the downloaded core, service-mode upgrades invoke the elevated service installer with --install-core <path> --sha256 <hex>. The digest is computed over the published file and rides the elevated command line, which no other local account can alter, so a file swapped after hashing is refused rather than staged. Sidecar-mode upgrades run the file directly and prompt for nothing. Rollback distinguishes staging that never ran, was refused, or succeeded: only a successful staging obliges the rollback to restore the approved copy too (deliberately not gated on the running mode, which reads NotRunning after the very crash being rolled back), and a refused staging restores the file so a retry does not see the new version on disk and conclude there is nothing to do. Requires clash-verge-service-ipc >= 2.6.7. The pinned dependency and the packaged service release must move together with this change, or the old installer silently treats --install-core as a plain install. * feat(installer): attest bundled cores to the service at install time prebuild computes sha256 digests of the downloaded mihomo sidecars into packages/windows/core-hashes.nsh (gitignored), included through the NSIS installerHooks path. After unpacking and before starting the service, the installer stages both cores into the service's approved directory with those digests, so what gets attested is exactly what this installer shipped: a core swapped on disk in a user-writable install directory is refused, not published. Failure is logged and does not abort the install; a later elevated service install can stage the cores again. * chore(deps): require clash-verge-service-ipc v2.6.8 v2.6.8 is the first service whose installer understands --install-core --sha256; the staging this branch introduces is inert against anything older, because the old installer treats the flag as a plain service installation and reports success. The service release and this pin therefore have to ship in the same app release. * fix(service): heap-allocate the core hashing buffer clippy::large_stack_arrays denies the 64 KiB stack array.
1398 lines
45 KiB
NSIS
1398 lines
45 KiB
NSIS
Unicode true
|
|
ManifestDPIAware true
|
|
; Add in `dpiAwareness` `PerMonitorV2` to manifest for Windows 10 1607+ (note this should not affect lower versions since they should be able to ignore this and pick up `dpiAware` `true` set by `ManifestDPIAware true`)
|
|
; Currently undocumented on NSIS's website but is in the Docs folder of source tree, see
|
|
; https://github.com/kichik/nsis/blob/5fc0b87b819a9eec006df4967d08e522ddd651c9/Docs/src/attributes.but#L286-L300
|
|
; https://github.com/tauri-apps/tauri/pull/10106
|
|
ManifestDPIAwareness PerMonitorV2
|
|
|
|
!if "{{compression}}" == "none"
|
|
SetCompress off
|
|
!else
|
|
; Set the compression algorithm. We default to LZMA.
|
|
SetCompressor /SOLID "{{compression}}"
|
|
!endif
|
|
|
|
!include MUI2.nsh
|
|
!include FileFunc.nsh
|
|
!include x64.nsh
|
|
!include WordFunc.nsh
|
|
!include "utils.nsh"
|
|
!include "FileAssociation.nsh"
|
|
!include "Win\COM.nsh"
|
|
!include "Win\Propkey.nsh"
|
|
!include "WinVer.nsh"
|
|
!include "LogicLib.nsh"
|
|
!include "StrFunc.nsh"
|
|
${StrCase}
|
|
${StrLoc}
|
|
|
|
!addplugindir "$%AppData%\Local\NSIS\"
|
|
|
|
{{#if installer_hooks}}
|
|
!include "{{installer_hooks}}"
|
|
{{/if}}
|
|
|
|
!define WEBVIEW2APPGUID "{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"
|
|
|
|
!define MANUFACTURER "{{manufacturer}}"
|
|
!define PRODUCTNAME "{{product_name}}"
|
|
!define VERSION "{{version}}"
|
|
!define VERSIONWITHBUILD "{{version_with_build}}"
|
|
!define SHORTDESCRIPTION "{{short_description}}"
|
|
!define HOMEPAGE "{{homepage}}"
|
|
!define INSTALLMODE "{{install_mode}}"
|
|
!define LICENSE "{{license}}"
|
|
!define INSTALLERICON "{{installer_icon}}"
|
|
!define SIDEBARIMAGE "{{sidebar_image}}"
|
|
!define HEADERIMAGE "{{header_image}}"
|
|
!define MAINBINARYNAME "{{main_binary_name}}"
|
|
!define MAINBINARYSRCPATH "{{main_binary_path}}"
|
|
!define BUNDLEID "{{bundle_id}}"
|
|
!define COPYRIGHT "{{copyright}}"
|
|
!define OUTFILE "{{out_file}}"
|
|
!define ARCH "{{arch}}"
|
|
!define ADDITIONALPLUGINSPATH "{{additional_plugins_path}}"
|
|
!define ALLOWDOWNGRADES "{{allow_downgrades}}"
|
|
!define DISPLAYLANGUAGESELECTOR "{{display_language_selector}}"
|
|
!define INSTALLWEBVIEW2MODE "{{install_webview2_mode}}"
|
|
!define WEBVIEW2INSTALLERARGS "{{webview2_installer_args}}"
|
|
!define WEBVIEW2BOOTSTRAPPERPATH "{{webview2_bootstrapper_path}}"
|
|
!define WEBVIEW2INSTALLERPATH "{{webview2_installer_path}}"
|
|
!define MINIMUMWEBVIEW2VERSION "{{minimum_webview2_version}}"
|
|
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCTNAME}"
|
|
!define MANUKEY "Software\${MANUFACTURER}"
|
|
!define MANUPRODUCTKEY "${MANUKEY}\${PRODUCTNAME}"
|
|
!define UNINSTALLERSIGNCOMMAND "{{uninstaller_sign_cmd}}"
|
|
!define ESTIMATEDSIZE "{{estimated_size}}"
|
|
!define STARTMENUFOLDER "{{start_menu_folder}}"
|
|
|
|
Var PassiveMode
|
|
Var UpdateMode
|
|
Var NoShortcutMode
|
|
Var WixMode
|
|
Var OldMainBinaryName
|
|
Var VC_REDIST_URL
|
|
Var VC_REDIST_EXE
|
|
Var VC_RUNTIME_READY
|
|
Var VC_RUNTIME_NEEDED
|
|
|
|
Name "${PRODUCTNAME}"
|
|
BrandingText "${COPYRIGHT}"
|
|
OutFile "${OUTFILE}"
|
|
|
|
; We don't actually use this value as default install path,
|
|
; it's just for nsis to append the product name folder in the directory selector
|
|
; https://nsis.sourceforge.io/Reference/InstallDir
|
|
!define PLACEHOLDER_INSTALL_DIR "placeholder\${PRODUCTNAME}"
|
|
InstallDir "${PLACEHOLDER_INSTALL_DIR}"
|
|
|
|
VIProductVersion "${VERSIONWITHBUILD}"
|
|
VIAddVersionKey "ProductName" "${PRODUCTNAME}"
|
|
VIAddVersionKey "FileDescription" "${SHORTDESCRIPTION}"
|
|
VIAddVersionKey "LegalCopyright" "${COPYRIGHT}"
|
|
VIAddVersionKey "FileVersion" "${VERSION}"
|
|
VIAddVersionKey "ProductVersion" "${VERSION}"
|
|
|
|
# additional plugins
|
|
!if "${ADDITIONALPLUGINSPATH}" != ""
|
|
!addplugindir "${ADDITIONALPLUGINSPATH}"
|
|
!endif
|
|
|
|
; Uninstaller signing command
|
|
!if "${UNINSTALLERSIGNCOMMAND}" != ""
|
|
!uninstfinalize '${UNINSTALLERSIGNCOMMAND}'
|
|
!endif
|
|
|
|
; Handle install mode, `perUser`, `perMachine` or `both`
|
|
!if "${INSTALLMODE}" == "perMachine"
|
|
RequestExecutionLevel admin
|
|
!endif
|
|
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
RequestExecutionLevel user
|
|
!endif
|
|
|
|
!if "${INSTALLMODE}" == "both"
|
|
!define MULTIUSER_MUI
|
|
!define MULTIUSER_INSTALLMODE_INSTDIR "${PRODUCTNAME}"
|
|
!define MULTIUSER_INSTALLMODE_COMMANDLINE
|
|
!if "${ARCH}" == "x64"
|
|
!define MULTIUSER_USE_PROGRAMFILES64
|
|
!else if "${ARCH}" == "arm64"
|
|
!define MULTIUSER_USE_PROGRAMFILES64
|
|
!endif
|
|
!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "${UNINSTKEY}"
|
|
!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "CurrentUser"
|
|
!define MULTIUSER_INSTALLMODEPAGE_SHOWUSERNAME
|
|
!define MULTIUSER_INSTALLMODE_FUNCTION RestorePreviousInstallLocation
|
|
!define MULTIUSER_EXECUTIONLEVEL Highest
|
|
!include MultiUser.nsh
|
|
!endif
|
|
|
|
; Installer icon
|
|
!if "${INSTALLERICON}" != ""
|
|
!define MUI_ICON "${INSTALLERICON}"
|
|
!endif
|
|
|
|
; Installer sidebar image
|
|
!if "${SIDEBARIMAGE}" != ""
|
|
!define MUI_WELCOMEFINISHPAGE_BITMAP "${SIDEBARIMAGE}"
|
|
!endif
|
|
|
|
; Installer header image
|
|
!if "${HEADERIMAGE}" != ""
|
|
!define MUI_HEADERIMAGE
|
|
!define MUI_HEADERIMAGE_BITMAP "${HEADERIMAGE}"
|
|
!endif
|
|
|
|
; Define registry key to store installer language
|
|
!define MUI_LANGDLL_REGISTRY_ROOT "HKCU"
|
|
!define MUI_LANGDLL_REGISTRY_KEY "${MANUPRODUCTKEY}"
|
|
!define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language"
|
|
|
|
; Installer pages, must be ordered as they appear
|
|
; 1. Welcome Page
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
|
!insertmacro MUI_PAGE_WELCOME
|
|
|
|
; 2. License Page (if defined)
|
|
!if "${LICENSE}" != ""
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
|
!insertmacro MUI_PAGE_LICENSE "${LICENSE}"
|
|
!endif
|
|
|
|
; 3. Install mode (if it is set to `both`)
|
|
!if "${INSTALLMODE}" == "both"
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
|
!insertmacro MULTIUSER_PAGE_INSTALLMODE
|
|
!endif
|
|
|
|
; 4. Custom page to ask user if he wants to reinstall/uninstall
|
|
; only if a previous installation was detected
|
|
Var ReinstallPageCheck
|
|
Page custom PageReinstall PageLeaveReinstall
|
|
Function PageReinstall
|
|
; Uninstall previous WiX installation if exists.
|
|
;
|
|
; A WiX installer stores the installation info in registry
|
|
; using a UUID and so we have to loop through all keys under
|
|
; `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall`
|
|
; and check if `DisplayName` and `Publisher` keys match ${PRODUCTNAME} and ${MANUFACTURER}
|
|
;
|
|
; This has a potential issue that there maybe another installation that matches
|
|
; our ${PRODUCTNAME} and ${MANUFACTURER} but wasn't installed by our WiX installer,
|
|
; however, this should be fine since the user will have to confirm the uninstallation
|
|
; and they can chose to abort it if doesn't make sense.
|
|
StrCpy $0 0
|
|
wix_loop:
|
|
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $0
|
|
StrCmp $1 "" wix_loop_done ; Exit loop if there is no more keys to loop on
|
|
IntOp $0 $0 + 1
|
|
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "DisplayName"
|
|
ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "Publisher"
|
|
StrCmp "$R0$R1" "${PRODUCTNAME}${MANUFACTURER}" 0 wix_loop
|
|
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1" "UninstallString"
|
|
${StrCase} $R1 $R0 "L"
|
|
${StrLoc} $R0 $R1 "msiexec" ">"
|
|
StrCmp $R0 0 0 wix_loop_done
|
|
StrCpy $WixMode 1
|
|
StrCpy $R6 "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$1"
|
|
Goto compare_version
|
|
wix_loop_done:
|
|
|
|
; Check if there is an existing installation, if not, abort the reinstall page
|
|
ReadRegStr $R0 SHCTX "${UNINSTKEY}" ""
|
|
ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString"
|
|
${IfThen} "$R0$R1" == "" ${|} Abort ${|}
|
|
|
|
; Compare this installar version with the existing installation
|
|
; and modify the messages presented to the user accordingly
|
|
compare_version:
|
|
StrCpy $R4 "$(older)"
|
|
${If} $WixMode = 1
|
|
ReadRegStr $R0 HKLM "$R6" "DisplayVersion"
|
|
${Else}
|
|
ReadRegStr $R0 SHCTX "${UNINSTKEY}" "DisplayVersion"
|
|
${EndIf}
|
|
${IfThen} $R0 == "" ${|} StrCpy $R4 "$(unknown)" ${|}
|
|
|
|
nsis_tauri_utils::SemverCompare "${VERSION}" $R0
|
|
Pop $R0
|
|
; Reinstalling the same version
|
|
${If} $R0 = 0
|
|
StrCpy $R1 "$(alreadyInstalledLong)"
|
|
StrCpy $R2 "$(addOrReinstall)"
|
|
StrCpy $R3 "$(uninstallApp)"
|
|
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(chooseMaintenanceOption)"
|
|
; Upgrading
|
|
${ElseIf} $R0 = 1
|
|
StrCpy $R1 "$(olderOrUnknownVersionInstalled)"
|
|
StrCpy $R2 "$(uninstallBeforeInstalling)"
|
|
StrCpy $R3 "$(dontUninstall)"
|
|
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)"
|
|
; Downgrading
|
|
${ElseIf} $R0 = -1
|
|
StrCpy $R1 "$(newerVersionInstalled)"
|
|
StrCpy $R2 "$(uninstallBeforeInstalling)"
|
|
!if "${ALLOWDOWNGRADES}" == "true"
|
|
StrCpy $R3 "$(dontUninstall)"
|
|
!else
|
|
StrCpy $R3 "$(dontUninstallDowngrade)"
|
|
!endif
|
|
!insertmacro MUI_HEADER_TEXT "$(alreadyInstalled)" "$(choowHowToInstall)"
|
|
${Else}
|
|
Abort
|
|
${EndIf}
|
|
|
|
; Skip showing the page if passive
|
|
;
|
|
; Note that we don't call this earlier at the begining
|
|
; of this function because we need to populate some variables
|
|
; related to current installed version if detected and whether
|
|
; we are downgrading or not.
|
|
${If} $PassiveMode = 1
|
|
Call PageLeaveReinstall
|
|
${Else}
|
|
nsDialogs::Create 1018
|
|
Pop $R4
|
|
${IfThen} $(^RTL) = 1 ${|} nsDialogs::SetRTL $(^RTL) ${|}
|
|
|
|
${NSD_CreateLabel} 0 0 100% 24u $R1
|
|
Pop $R1
|
|
|
|
${NSD_CreateRadioButton} 30u 50u -30u 8u $R2
|
|
Pop $R2
|
|
${NSD_OnClick} $R2 PageReinstallUpdateSelection
|
|
|
|
${NSD_CreateRadioButton} 30u 70u -30u 8u $R3
|
|
Pop $R3
|
|
; Disable this radio button if downgrading and downgrades are disabled
|
|
!if "${ALLOWDOWNGRADES}" == "false"
|
|
${IfThen} $R0 = -1 ${|} EnableWindow $R3 0 ${|}
|
|
!endif
|
|
${NSD_OnClick} $R3 PageReinstallUpdateSelection
|
|
|
|
; Check the first radio button if this the first time
|
|
; we enter this page or if the second button wasn't
|
|
; selected the last time we were on this page
|
|
${If} $ReinstallPageCheck <> 2
|
|
SendMessage $R2 ${BM_SETCHECK} ${BST_CHECKED} 0
|
|
${Else}
|
|
SendMessage $R3 ${BM_SETCHECK} ${BST_CHECKED} 0
|
|
${EndIf}
|
|
|
|
${NSD_SetFocus} $R2
|
|
nsDialogs::Show
|
|
${EndIf}
|
|
FunctionEnd
|
|
Function PageReinstallUpdateSelection
|
|
${NSD_GetState} $R2 $R1
|
|
${If} $R1 == ${BST_CHECKED}
|
|
StrCpy $ReinstallPageCheck 1
|
|
${Else}
|
|
StrCpy $ReinstallPageCheck 2
|
|
${EndIf}
|
|
FunctionEnd
|
|
Function PageLeaveReinstall
|
|
${NSD_GetState} $R2 $R1
|
|
|
|
; If migrating from Wix, always uninstall
|
|
${If} $WixMode = 1
|
|
Goto reinst_uninstall
|
|
${EndIf}
|
|
|
|
; In update mode, always proceeds without uninstalling
|
|
${If} $UpdateMode = 1
|
|
Goto reinst_done
|
|
${EndIf}
|
|
|
|
; $R0 holds whether same(0)/upgrading(1)/downgrading(-1) version
|
|
; $R1 holds the radio buttons state:
|
|
; 1 => first choice was selected
|
|
; 0 => second choice was selected
|
|
${If} $R0 = 0 ; Same version, proceed
|
|
${If} $R1 = 1 ; User chose to add/reinstall
|
|
Goto reinst_done
|
|
${Else} ; User chose to uninstall
|
|
Goto reinst_uninstall
|
|
${EndIf}
|
|
${ElseIf} $R0 = 1 ; Upgrading
|
|
${If} $R1 = 1 ; User chose to uninstall
|
|
Goto reinst_uninstall
|
|
${Else}
|
|
Goto reinst_done ; User chose NOT to uninstall
|
|
${EndIf}
|
|
${ElseIf} $R0 = -1 ; Downgrading
|
|
${If} $R1 = 1 ; User chose to uninstall
|
|
Goto reinst_uninstall
|
|
${Else}
|
|
Goto reinst_done ; User chose NOT to uninstall
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
reinst_uninstall:
|
|
HideWindow
|
|
ClearErrors
|
|
|
|
${If} $WixMode = 1
|
|
ReadRegStr $R1 HKLM "$R6" "UninstallString"
|
|
ExecWait '$R1' $0
|
|
${Else}
|
|
ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" ""
|
|
ReadRegStr $R1 SHCTX "${UNINSTKEY}" "UninstallString"
|
|
${IfThen} $UpdateMode = 1 ${|} StrCpy $R1 "$R1 /UPDATE" ${|} ; append /UPDATE
|
|
${IfThen} $PassiveMode = 1 ${|} StrCpy $R1 "$R1 /P" ${|} ; append /P
|
|
StrCpy $R1 "$R1 _?=$4" ; append uninstall directory
|
|
ExecWait '$R1' $0
|
|
${EndIf}
|
|
|
|
BringToFront
|
|
|
|
${IfThen} ${Errors} ${|} StrCpy $0 2 ${|} ; ExecWait failed, set fake exit code
|
|
|
|
${If} $0 <> 0
|
|
${OrIf} ${FileExists} "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
; User cancelled wix uninstaller? return to select un/reinstall page
|
|
${If} $WixMode = 1
|
|
${AndIf} $0 = 1602
|
|
Abort
|
|
${EndIf}
|
|
|
|
; User cancelled NSIS uninstaller? return to select un/reinstall page
|
|
${If} $0 = 1
|
|
Abort
|
|
${EndIf}
|
|
|
|
; Other erros? show generic error message and return to select un/reinstall page
|
|
MessageBox MB_ICONEXCLAMATION "$(unableToUninstall)"
|
|
Abort
|
|
${EndIf}
|
|
reinst_done:
|
|
FunctionEnd
|
|
|
|
; 5. Choose install directory page
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
|
!insertmacro MUI_PAGE_DIRECTORY
|
|
|
|
; 6. Start menu shortcut page
|
|
Var AppStartMenuFolder
|
|
!if "${STARTMENUFOLDER}" != ""
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
|
!define MUI_STARTMENUPAGE_DEFAULTFOLDER "${STARTMENUFOLDER}"
|
|
!else
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE Skip
|
|
!endif
|
|
!insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder
|
|
|
|
; 7. Installation page
|
|
!insertmacro MUI_PAGE_INSTFILES
|
|
|
|
; 8. Finish page
|
|
;
|
|
; Don't auto jump to finish page after installation page,
|
|
; because the installation page has useful info that can be used debug any issues with the installer.
|
|
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
|
; Use show readme button in the finish page as a button create a desktop shortcut
|
|
!define MUI_FINISHPAGE_SHOWREADME
|
|
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(createDesktop)"
|
|
!define MUI_FINISHPAGE_SHOWREADME_FUNCTION CreateOrUpdateDesktopShortcut
|
|
; Show run app after installation.
|
|
!define MUI_FINISHPAGE_RUN
|
|
!define MUI_FINISHPAGE_RUN_FUNCTION RunMainBinary
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
|
|
!insertmacro MUI_PAGE_FINISH
|
|
|
|
Function RunMainBinary
|
|
nsis_tauri_utils::RunAsUser "$INSTDIR\${MAINBINARYNAME}.exe" ""
|
|
FunctionEnd
|
|
|
|
; Uninstaller Pages
|
|
; 1. Confirm uninstall page
|
|
Var DeleteAppDataCheckbox
|
|
Var DeleteAppDataCheckboxState
|
|
!define /ifndef WS_EX_LAYOUTRTL 0x00400000
|
|
!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.ConfirmShow
|
|
Function un.ConfirmShow ; Add add a `Delete app data` check box
|
|
; $1 inner dialog HWND
|
|
; $2 window DPI
|
|
; $3 style
|
|
; $4 x
|
|
; $5 y
|
|
; $6 width
|
|
; $7 height
|
|
FindWindow $1 "#32770" "" $HWNDPARENT ; Find inner dialog
|
|
System::Call "user32::GetDpiForWindow(p r1) i .r2"
|
|
${If} $(^RTL) = 1
|
|
StrCpy $3 "${__NSD_CheckBox_EXSTYLE} | ${WS_EX_LAYOUTRTL}"
|
|
IntOp $4 50 * $2
|
|
${Else}
|
|
StrCpy $3 "${__NSD_CheckBox_EXSTYLE}"
|
|
IntOp $4 0 * $2
|
|
${EndIf}
|
|
IntOp $5 100 * $2
|
|
IntOp $6 400 * $2
|
|
IntOp $7 25 * $2
|
|
IntOp $4 $4 / 96
|
|
IntOp $5 $5 / 96
|
|
IntOp $6 $6 / 96
|
|
IntOp $7 $7 / 96
|
|
System::Call 'user32::CreateWindowEx(i r3, w "${__NSD_CheckBox_CLASS}", w "$(deleteAppData)", i ${__NSD_CheckBox_STYLE}, i r4, i r5, i r6, i r7, p r1, i0, i0, i0) i .s'
|
|
Pop $DeleteAppDataCheckbox
|
|
SendMessage $HWNDPARENT ${WM_GETFONT} 0 0 $1
|
|
SendMessage $DeleteAppDataCheckbox ${WM_SETFONT} $1 1
|
|
FunctionEnd
|
|
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.ConfirmLeave
|
|
Function un.ConfirmLeave
|
|
SendMessage $DeleteAppDataCheckbox ${BM_GETCHECK} 0 0 $DeleteAppDataCheckboxState
|
|
FunctionEnd
|
|
!define MUI_PAGE_CUSTOMFUNCTION_PRE un.SkipIfPassive
|
|
!insertmacro MUI_UNPAGE_CONFIRM
|
|
|
|
; 2. Uninstalling Page
|
|
!insertmacro MUI_UNPAGE_INSTFILES
|
|
|
|
;Languages
|
|
{{#each languages}}
|
|
!insertmacro MUI_LANGUAGE "{{this}}"
|
|
{{/each}}
|
|
!insertmacro MUI_RESERVEFILE_LANGDLL
|
|
{{#each language_files}}
|
|
!include "{{this}}"
|
|
{{/each}}
|
|
|
|
Function .onInit
|
|
${GetOptions} $CMDLINE "/P" $PassiveMode
|
|
${IfNot} ${Errors}
|
|
StrCpy $PassiveMode 1
|
|
${EndIf}
|
|
|
|
${GetOptions} $CMDLINE "/NS" $NoShortcutMode
|
|
${IfNot} ${Errors}
|
|
StrCpy $NoShortcutMode 1
|
|
${EndIf}
|
|
|
|
${GetOptions} $CMDLINE "/UPDATE" $UpdateMode
|
|
${IfNot} ${Errors}
|
|
StrCpy $UpdateMode 1
|
|
${EndIf}
|
|
|
|
!if "${DISPLAYLANGUAGESELECTOR}" == "true"
|
|
; Auto-update forwards the app's UI language as `/LANG=<NSIS-lang-id>` so
|
|
; the installer uses it directly and skips the interactive language
|
|
; selector, letting the update start without prompting the user.
|
|
; See `src-tauri/src/core/updater.rs` (`nsis_language_id`).
|
|
${GetOptions} $CMDLINE "/LANG=" $0
|
|
${IfNot} ${Errors}
|
|
${If} $0 == "1033"
|
|
${OrIf} $0 == "1049"
|
|
${OrIf} $0 == "2052"
|
|
StrCpy $LANGUAGE $0
|
|
${Else}
|
|
!insertmacro MUI_LANGDLL_DISPLAY
|
|
${EndIf}
|
|
${Else}
|
|
!insertmacro MUI_LANGDLL_DISPLAY
|
|
${EndIf}
|
|
!endif
|
|
|
|
!insertmacro SetContext
|
|
|
|
${If} $INSTDIR == "${PLACEHOLDER_INSTALL_DIR}"
|
|
; Set default install location
|
|
!if "${INSTALLMODE}" == "perMachine"
|
|
${If} ${RunningX64}
|
|
!if "${ARCH}" == "x64"
|
|
StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}"
|
|
!else if "${ARCH}" == "arm64"
|
|
StrCpy $INSTDIR "$PROGRAMFILES64\${PRODUCTNAME}"
|
|
!else
|
|
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}"
|
|
!endif
|
|
${Else}
|
|
StrCpy $INSTDIR "$PROGRAMFILES\${PRODUCTNAME}"
|
|
${EndIf}
|
|
!else if "${INSTALLMODE}" == "currentUser"
|
|
StrCpy $INSTDIR "$LOCALAPPDATA\${PRODUCTNAME}"
|
|
!endif
|
|
|
|
Call RestorePreviousInstallLocation
|
|
${EndIf}
|
|
|
|
|
|
!if "${INSTALLMODE}" == "both"
|
|
!insertmacro MULTIUSER_INIT
|
|
!endif
|
|
FunctionEnd
|
|
|
|
|
|
Function CheckVCRuntime64
|
|
Push $R0
|
|
Push $R1
|
|
StrCpy $VC_RUNTIME_READY "0"
|
|
StrCpy $R1 "$WINDIR\Sysnative"
|
|
IfFileExists "$R1\kernel32.dll" 0 +3
|
|
IfFileExists "$R1\vcruntime140.dll" 0 missing
|
|
IfFileExists "$R1\msvcp140.dll" 0 missing
|
|
Goto found
|
|
StrCpy $R1 "$WINDIR\System32"
|
|
IfFileExists "$R1\vcruntime140.dll" 0 missing
|
|
IfFileExists "$R1\msvcp140.dll" 0 missing
|
|
found:
|
|
StrCpy $VC_RUNTIME_READY "1"
|
|
Goto done
|
|
missing:
|
|
StrCpy $VC_RUNTIME_READY "0"
|
|
done:
|
|
Pop $R1
|
|
Pop $R0
|
|
FunctionEnd
|
|
|
|
|
|
!macro CheckAllVergeProcesses
|
|
; Check if clash-verge-service.exe is running
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::FindProcessCurrentUser "clash-verge-service.exe"
|
|
!else
|
|
nsis_tauri_utils::FindProcess "clash-verge-service.exe"
|
|
!endif
|
|
Pop $R0
|
|
${If} $R0 = 0
|
|
DetailPrint "Kill clash-verge-service.exe..."
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::KillProcessCurrentUser "clash-verge-service.exe"
|
|
!else
|
|
nsis_tauri_utils::KillProcess "clash-verge-service.exe"
|
|
!endif
|
|
${EndIf}
|
|
|
|
; Check if verge-mihomo-alpha.exe is running
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::FindProcessCurrentUser "verge-mihomo-alpha.exe"
|
|
!else
|
|
nsis_tauri_utils::FindProcess "verge-mihomo-alpha.exe"
|
|
!endif
|
|
Pop $R0
|
|
${If} $R0 = 0
|
|
DetailPrint "Kill verge-mihomo-alpha.exe..."
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::KillProcessCurrentUser "verge-mihomo-alpha.exe"
|
|
!else
|
|
nsis_tauri_utils::KillProcess "verge-mihomo-alpha.exe"
|
|
!endif
|
|
${EndIf}
|
|
|
|
; Check if verge-mihomo.exe is running
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::FindProcessCurrentUser "verge-mihomo.exe"
|
|
!else
|
|
nsis_tauri_utils::FindProcess "verge-mihomo.exe"
|
|
!endif
|
|
Pop $R0
|
|
${If} $R0 = 0
|
|
DetailPrint "Kill verge-mihomo.exe..."
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::KillProcessCurrentUser "verge-mihomo.exe"
|
|
!else
|
|
nsis_tauri_utils::KillProcess "verge-mihomo.exe"
|
|
!endif
|
|
${EndIf}
|
|
|
|
; Check if clash-meta-alpha.exe is running
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::FindProcessCurrentUser "clash-meta-alpha.exe"
|
|
!else
|
|
nsis_tauri_utils::FindProcess "clash-meta-alpha.exe"
|
|
!endif
|
|
Pop $R0
|
|
${If} $R0 = 0
|
|
DetailPrint "Kill clash-meta-alpha.exe..."
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::KillProcessCurrentUser "clash-meta-alpha.exe"
|
|
!else
|
|
nsis_tauri_utils::KillProcess "clash-meta-alpha.exe"
|
|
!endif
|
|
${EndIf}
|
|
|
|
; Check if clash-meta.exe is running
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::FindProcessCurrentUser "clash-meta.exe"
|
|
!else
|
|
nsis_tauri_utils::FindProcess "clash-meta.exe"
|
|
!endif
|
|
Pop $R0
|
|
${If} $R0 = 0
|
|
DetailPrint "Kill clash-meta.exe..."
|
|
!if "${INSTALLMODE}" == "currentUser"
|
|
nsis_tauri_utils::KillProcessCurrentUser "clash-meta.exe"
|
|
!else
|
|
nsis_tauri_utils::KillProcess "clash-meta.exe"
|
|
!endif
|
|
${EndIf}
|
|
!macroend
|
|
|
|
!macro StartVergeService
|
|
; Check if the service exists
|
|
SimpleSC::ExistsService "clash_verge_service"
|
|
Pop $0 ; 0: service exists; other: service not exists
|
|
; Service exists
|
|
${If} $0 == 0
|
|
Push $0
|
|
; Check if the service is running
|
|
SimpleSC::ServiceIsRunning "clash_verge_service"
|
|
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
|
|
Pop $1 ; returns 1 (service is running) - returns 0 (service is not running)
|
|
${If} $0 == 0
|
|
Push $0
|
|
${If} $1 == 0
|
|
DetailPrint "Restart ${PRODUCTNAME} Service..."
|
|
SimpleSC::StartService "clash_verge_service" "" 30
|
|
${EndIf}
|
|
${ElseIf} $0 != 0
|
|
Push $0
|
|
SimpleSC::GetErrorMessage
|
|
Pop $0
|
|
MessageBox MB_OK|MB_ICONSTOP "Check Service Status Error ($0)"
|
|
${EndIf}
|
|
${EndIf}
|
|
!macroend
|
|
|
|
!macro RemoveVergeService
|
|
; Check if the service exists
|
|
SimpleSC::ExistsService "clash_verge_service"
|
|
Pop $0 ; 0: service exists; other: service not exists
|
|
; Service exists
|
|
${If} $0 == 0
|
|
Push $0
|
|
; Check if the service is running
|
|
SimpleSC::ServiceIsRunning "clash_verge_service"
|
|
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
|
|
Pop $1 ; returns 1 (service is running) - returns 0 (service is not running)
|
|
${If} $0 == 0
|
|
Push $0
|
|
${If} $1 == 1
|
|
DetailPrint "Stop ${PRODUCTNAME} Service..."
|
|
SimpleSC::StopService "clash_verge_service" 1 30
|
|
Pop $0 ; returns an errorcode (<>0) otherwise success (0)
|
|
${If} $0 == 0
|
|
DetailPrint "Removing ${PRODUCTNAME} Service..."
|
|
SimpleSC::RemoveService "clash_verge_service"
|
|
${ElseIf} $0 != 0
|
|
Push $0
|
|
SimpleSC::GetErrorMessage
|
|
Pop $0
|
|
MessageBox MB_OK|MB_ICONSTOP "${PRODUCTNAME} Service Stop Error ($0)"
|
|
${EndIf}
|
|
${ElseIf} $1 == 0
|
|
DetailPrint "Removing ${PRODUCTNAME} Service..."
|
|
SimpleSC::RemoveService "clash_verge_service"
|
|
${EndIf}
|
|
${ElseIf} $0 != 0
|
|
Push $0
|
|
SimpleSC::GetErrorMessage
|
|
Pop $0
|
|
MessageBox MB_OK|MB_ICONSTOP "Check Service Status Error ($0)"
|
|
${EndIf}
|
|
${EndIf}
|
|
!macroend
|
|
|
|
Section EarlyChecks
|
|
; Abort silent installer if downgrades is disabled
|
|
!if "${ALLOWDOWNGRADES}" == "false"
|
|
${If} ${Silent}
|
|
; If downgrading
|
|
${If} $R0 = -1
|
|
System::Call 'kernel32::AttachConsole(i -1)i.r0'
|
|
${If} $0 <> 0
|
|
System::Call 'kernel32::GetStdHandle(i -11)i.r0'
|
|
System::call 'kernel32::SetConsoleTextAttribute(i r0, i 0x0004)' ; set red color
|
|
FileWrite $0 "$(silentDowngrades)"
|
|
${EndIf}
|
|
Abort
|
|
${EndIf}
|
|
${EndIf}
|
|
!endif
|
|
|
|
SectionEnd
|
|
|
|
Section CheckAndInstallVSRuntime
|
|
StrCpy $VC_RUNTIME_NEEDED "0"
|
|
|
|
${If} ${IsNativeARM64}
|
|
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.arm64.exe"
|
|
StrCpy $VC_REDIST_EXE "vc_redist.arm64.exe"
|
|
Call CheckVCRuntime64
|
|
${If} $VC_RUNTIME_READY != "1"
|
|
StrCpy $VC_RUNTIME_NEEDED "1"
|
|
${EndIf}
|
|
|
|
${ElseIf} ${RunningX64}
|
|
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x64.exe"
|
|
StrCpy $VC_REDIST_EXE "vc_redist.x64.exe"
|
|
Call CheckVCRuntime64
|
|
${If} $VC_RUNTIME_READY != "1"
|
|
StrCpy $VC_RUNTIME_NEEDED "1"
|
|
${EndIf}
|
|
|
|
${Else}
|
|
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x86.exe"
|
|
StrCpy $VC_REDIST_EXE "vc_redist.x86.exe"
|
|
|
|
IfFileExists "$SYSDIR\vcruntime140.dll" 0 filesMissing32
|
|
IfFileExists "$SYSDIR\msvcp140.dll" 0 filesMissing32
|
|
Goto afterFileCheck32
|
|
filesMissing32:
|
|
StrCpy $VC_RUNTIME_NEEDED "1"
|
|
afterFileCheck32:
|
|
${EndIf}
|
|
|
|
${If} $VC_RUNTIME_NEEDED != "1"
|
|
${If} ${IsNativeARM64}
|
|
SetRegView 64
|
|
ClearErrors
|
|
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\arm64" "Installed"
|
|
${If} ${Errors}
|
|
StrCpy $R0 0
|
|
${EndIf}
|
|
SetRegView 32
|
|
${ElseIf} ${RunningX64}
|
|
SetRegView 64
|
|
ClearErrors
|
|
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${ARCH}" "Installed"
|
|
${If} ${Errors}
|
|
StrCpy $R0 0
|
|
${EndIf}
|
|
SetRegView 32
|
|
${Else}
|
|
ClearErrors
|
|
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Installed"
|
|
${If} ${Errors}
|
|
StrCpy $R0 0
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
${If} $R0 != "1"
|
|
StrCpy $VC_RUNTIME_NEEDED "1"
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
${If} $VC_RUNTIME_NEEDED != "1"
|
|
DetailPrint "已检测到匹配的 Visual C++ Redistributable,跳过安装"
|
|
Goto done_vc
|
|
${EndIf}
|
|
|
|
DetailPrint "正在下载 Visual C++ Redistributable..."
|
|
nsisdl::download "$VC_REDIST_URL" "$TEMP\$VC_REDIST_EXE"
|
|
Pop $0
|
|
${If} $0 == "success"
|
|
DetailPrint "正在安装 Visual C++ Redistributable..."
|
|
ExecWait '"$TEMP\$VC_REDIST_EXE" /quiet /norestart' $0
|
|
${If} $0 == 0
|
|
DetailPrint "Visual C++ Redistributable 安装成功"
|
|
${Else}
|
|
DetailPrint "Visual C++ Redistributable 安装失败"
|
|
${EndIf}
|
|
Delete "$TEMP\$VC_REDIST_EXE"
|
|
${Else}
|
|
DetailPrint "Visual C++ Redistributable 下载失败"
|
|
${EndIf}
|
|
|
|
done_vc:
|
|
SectionEnd
|
|
|
|
Section WebView2
|
|
; Check if Webview2 is already installed and skip this section
|
|
${If} ${RunningX64}
|
|
ReadRegStr $4 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
|
|
${Else}
|
|
ReadRegStr $4 HKLM "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
|
|
${EndIf}
|
|
${If} $4 == ""
|
|
ReadRegStr $4 HKCU "SOFTWARE\Microsoft\EdgeUpdate\Clients\${WEBVIEW2APPGUID}" "pv"
|
|
${EndIf}
|
|
|
|
${If} $4 == ""
|
|
; Webview2 installation
|
|
;
|
|
; Skip if updating
|
|
${If} $UpdateMode <> 1
|
|
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
|
|
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
|
DetailPrint "$(webview2Downloading)"
|
|
NSISdl::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
|
Pop $0
|
|
${If} $0 == "success"
|
|
DetailPrint "$(webview2DownloadSuccess)"
|
|
${Else}
|
|
DetailPrint "$(webview2DownloadError)"
|
|
Abort "$(webview2AbortError)"
|
|
${EndIf}
|
|
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
|
Goto install_webview2
|
|
!endif
|
|
|
|
!if "${INSTALLWEBVIEW2MODE}" == "embedBootstrapper"
|
|
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
|
File "/oname=$TEMP\MicrosoftEdgeWebview2Setup.exe" "${WEBVIEW2BOOTSTRAPPERPATH}"
|
|
DetailPrint "$(installingWebview2)"
|
|
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe"
|
|
Goto install_webview2
|
|
!endif
|
|
|
|
!if "${INSTALLWEBVIEW2MODE}" == "offlineInstaller"
|
|
Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
|
|
File "/oname=$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" "${WEBVIEW2INSTALLERPATH}"
|
|
DetailPrint "$(installingWebview2)"
|
|
StrCpy $6 "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
|
|
Goto install_webview2
|
|
!endif
|
|
|
|
Goto webview2_done
|
|
|
|
install_webview2:
|
|
DetailPrint "$(installingWebview2)"
|
|
; $6 holds the path to the webview2 installer
|
|
ExecWait "$6 ${WEBVIEW2INSTALLERARGS} /install" $1
|
|
${If} $1 = 0
|
|
DetailPrint "$(webview2InstallSuccess)"
|
|
${Else}
|
|
DetailPrint "$(webview2InstallError)"
|
|
Abort "$(webview2AbortError)"
|
|
${EndIf}
|
|
webview2_done:
|
|
${EndIf}
|
|
${Else}
|
|
!if "${MINIMUMWEBVIEW2VERSION}" != ""
|
|
${VersionCompare} "${MINIMUMWEBVIEW2VERSION}" "$4" $R0
|
|
${If} $R0 = 1
|
|
update_webview:
|
|
DetailPrint "$(installingWebview2)"
|
|
${If} ${RunningX64}
|
|
ReadRegStr $R1 HKLM "SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate" "path"
|
|
${Else}
|
|
ReadRegStr $R1 HKLM "SOFTWARE\Microsoft\EdgeUpdate" "path"
|
|
${EndIf}
|
|
${If} $R1 == ""
|
|
ReadRegStr $R1 HKCU "SOFTWARE\Microsoft\EdgeUpdate" "path"
|
|
${EndIf}
|
|
${If} $R1 != ""
|
|
; Chromium updater docs: https://source.chromium.org/chromium/chromium/src/+/main:docs/updater/user_manual.md
|
|
; Modified from "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView\ModifyPath"
|
|
ExecWait `"$R1" /install appguid=${WEBVIEW2APPGUID}&needsadmin=true` $1
|
|
${If} $1 = 0
|
|
DetailPrint "$(webview2InstallSuccess)"
|
|
${Else}
|
|
MessageBox MB_ICONEXCLAMATION|MB_ABORTRETRYIGNORE "$(webview2InstallError)" IDIGNORE ignore IDRETRY update_webview
|
|
Quit
|
|
ignore:
|
|
${EndIf}
|
|
${EndIf}
|
|
${EndIf}
|
|
!endif
|
|
${EndIf}
|
|
SectionEnd
|
|
|
|
Section Install
|
|
SetOutPath $INSTDIR
|
|
|
|
!ifmacrodef NSIS_HOOK_PREINSTALL
|
|
!insertmacro NSIS_HOOK_PREINSTALL
|
|
!endif
|
|
|
|
nsExec::Exec 'netsh int tcp res'
|
|
|
|
!insertmacro CheckIfAppIsRunning "${MAINBINARYNAME}.exe" "${PRODUCTNAME}"
|
|
!insertmacro CheckAllVergeProcesses
|
|
|
|
; Ensure startup folders exist
|
|
CreateDirectory "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
|
|
DetailPrint "Ensured system startup folder exists"
|
|
|
|
SetShellVarContext current
|
|
StrCpy $0 "$SMPROGRAMS\Startup"
|
|
CreateDirectory "$0"
|
|
DetailPrint "Ensured user startup folder exists: $0"
|
|
|
|
; Remove stale window-state files
|
|
DetailPrint "Removing window-state.json / .window-state.json"
|
|
Delete "$APPDATA\io.github.clash-verge-rev.clash-verge-rev\window-state.json"
|
|
Delete "$APPDATA\io.github.clash-verge-rev.clash-verge-rev\.window-state.json"
|
|
|
|
; Clean legacy auto-launch registry entries
|
|
StrCpy $R1 "Software\Microsoft\Windows\CurrentVersion\Run"
|
|
|
|
SetRegView 64
|
|
ReadRegStr $R2 HKCU "$R1" "Clash Verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKCU "$R1" "Clash Verge"
|
|
${EndIf}
|
|
ReadRegStr $R2 HKLM "$R1" "Clash Verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKLM "$R1" "Clash Verge"
|
|
${EndIf}
|
|
ReadRegStr $R2 HKCU "$R1" "clash-verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKCU "$R1" "clash-verge"
|
|
${EndIf}
|
|
ReadRegStr $R2 HKLM "$R1" "clash-verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKLM "$R1" "clash-verge"
|
|
${EndIf}
|
|
|
|
; Remove legacy executables
|
|
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
|
|
Delete "$INSTDIR\Clash Verge.exe"
|
|
|
|
!insertmacro SetContext
|
|
|
|
; Copy main executable
|
|
File "${MAINBINARYSRCPATH}"
|
|
|
|
; Copy resources
|
|
{{#each resources_dirs}}
|
|
CreateDirectory "$INSTDIR\\{{this}}"
|
|
{{/each}}
|
|
{{#each resources}}
|
|
File /a "/oname={{this.[1]}}" "{{no-escape @key}}"
|
|
{{/each}}
|
|
|
|
; Copy external binaries
|
|
{{#each binaries}}
|
|
File /a "/oname={{this}}" "{{no-escape @key}}"
|
|
{{/each}}
|
|
|
|
; Publish the bundled cores into the service's approved directory before the service starts.
|
|
; The service only executes administrator-approved copies, and the build-time digests
|
|
; (core-hashes.nsh, generated by scripts/prebuild.mjs, included via installerHooks) attest
|
|
; exactly the bytes this installer unpacked: a core swapped on disk is refused, not published.
|
|
; Not fatal on failure; a later elevated service install can stage the cores again.
|
|
!ifdef MIHOMO_SHA256
|
|
DetailPrint "Staging verge-mihomo for ${PRODUCTNAME} Service..."
|
|
nsExec::ExecToLog '"$INSTDIR\resources\clash-verge-service-install.exe" --install-core "$INSTDIR\verge-mihomo.exe" --sha256 "${MIHOMO_SHA256}"'
|
|
Pop $0
|
|
${IfThen} $0 != 0 ${|} DetailPrint "Staging verge-mihomo returned $0" ${|}
|
|
!endif
|
|
!ifdef MIHOMO_ALPHA_SHA256
|
|
DetailPrint "Staging verge-mihomo-alpha for ${PRODUCTNAME} Service..."
|
|
nsExec::ExecToLog '"$INSTDIR\resources\clash-verge-service-install.exe" --install-core "$INSTDIR\verge-mihomo-alpha.exe" --sha256 "${MIHOMO_ALPHA_SHA256}"'
|
|
Pop $0
|
|
${IfThen} $0 != 0 ${|} DetailPrint "Staging verge-mihomo-alpha returned $0" ${|}
|
|
!endif
|
|
|
|
!insertmacro StartVergeService
|
|
|
|
; Create file associations
|
|
{{#each file_associations as |association| ~}}
|
|
{{#each association.ext as |ext| ~}}
|
|
!insertmacro APP_ASSOCIATE "{{ext}}" "{{or association.name ext}}" "{{association-description association.description ext}}" "$INSTDIR\${MAINBINARYNAME}.exe,0" "Open with ${PRODUCTNAME}" "$INSTDIR\${MAINBINARYNAME}.exe $\"%1$\""
|
|
{{/each}}
|
|
{{/each}}
|
|
|
|
; Register deep links
|
|
{{#each deep_link_protocols as |protocol| ~}}
|
|
WriteRegStr SHCTX "Software\Classes\\{{protocol}}" "URL Protocol" ""
|
|
WriteRegStr SHCTX "Software\Classes\\{{protocol}}" "" "URL:${BUNDLEID} protocol"
|
|
WriteRegStr SHCTX "Software\Classes\\{{protocol}}\DefaultIcon" "" "$\"$INSTDIR\${MAINBINARYNAME}.exe$\",0"
|
|
WriteRegStr SHCTX "Software\Classes\\{{protocol}}\shell\open\command" "" "$\"$INSTDIR\${MAINBINARYNAME}.exe$\" $\"%1$\""
|
|
{{/each}}
|
|
|
|
; Create uninstaller
|
|
WriteUninstaller "$INSTDIR\uninstall.exe"
|
|
|
|
; Save $INSTDIR in registry for future installations
|
|
WriteRegStr SHCTX "${MANUPRODUCTKEY}" "" $INSTDIR
|
|
|
|
!if "${INSTALLMODE}" == "both"
|
|
; Save install mode to be selected by default for the next installation such as updating
|
|
; or when uninstalling
|
|
WriteRegStr SHCTX "${UNINSTKEY}" $MultiUser.InstallMode 1
|
|
!endif
|
|
|
|
; Remove old main binary if it doesn't match new main binary name
|
|
ReadRegStr $OldMainBinaryName SHCTX "${UNINSTKEY}" "MainBinaryName"
|
|
${If} $OldMainBinaryName != ""
|
|
${AndIf} $OldMainBinaryName != "${MAINBINARYNAME}.exe"
|
|
Delete "$INSTDIR\$OldMainBinaryName"
|
|
${EndIf}
|
|
|
|
; Save current MAINBINARYNAME for future updates
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "MainBinaryName" "${MAINBINARYNAME}.exe"
|
|
|
|
; Registry information for add/remove programs
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "DisplayName" "${PRODUCTNAME}"
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "DisplayIcon" "$\"$INSTDIR\${MAINBINARYNAME}.exe$\""
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "DisplayVersion" "${VERSION}"
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "Publisher" "${MANUFACTURER}"
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "InstallLocation" "$\"$INSTDIR$\""
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
|
|
WriteRegDWORD SHCTX "${UNINSTKEY}" "NoModify" "1"
|
|
WriteRegDWORD SHCTX "${UNINSTKEY}" "NoRepair" "1"
|
|
|
|
${GetSize} "$INSTDIR" "/M=uninstall.exe /S=0K /G=0" $0 $1 $2
|
|
IntOp $0 $0 + ${ESTIMATEDSIZE}
|
|
IntFmt $0 "0x%08X" $0
|
|
WriteRegDWORD SHCTX "${UNINSTKEY}" "EstimatedSize" "$0"
|
|
|
|
!if "${HOMEPAGE}" != ""
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "URLInfoAbout" "${HOMEPAGE}"
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "URLUpdateInfo" "${HOMEPAGE}"
|
|
WriteRegStr SHCTX "${UNINSTKEY}" "HelpLink" "${HOMEPAGE}"
|
|
!endif
|
|
|
|
; Create start menu shortcut
|
|
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
|
Call CreateOrUpdateStartMenuShortcut
|
|
!insertmacro MUI_STARTMENU_WRITE_END
|
|
|
|
; Create desktop shortcut for silent and passive installers
|
|
; because finish page will be skipped
|
|
${If} $PassiveMode = 1
|
|
${OrIf} ${Silent}
|
|
Call CreateOrUpdateDesktopShortcut
|
|
${EndIf}
|
|
|
|
!ifmacrodef NSIS_HOOK_POSTINSTALL
|
|
!insertmacro NSIS_HOOK_POSTINSTALL
|
|
!endif
|
|
|
|
; Auto close this page for passive mode
|
|
${If} $PassiveMode = 1
|
|
SetAutoClose true
|
|
${EndIf}
|
|
SectionEnd
|
|
|
|
Function .onInstSuccess
|
|
; Check for `/R` flag only in silent and passive installers because
|
|
; GUI installer has a toggle for the user to (re)start the app
|
|
${If} $PassiveMode = 1
|
|
${OrIf} ${Silent}
|
|
${GetOptions} $CMDLINE "/R" $R0
|
|
${IfNot} ${Errors}
|
|
${GetOptions} $CMDLINE "/ARGS" $R0
|
|
nsis_tauri_utils::RunAsUser "$INSTDIR\${MAINBINARYNAME}.exe" "$R0"
|
|
${EndIf}
|
|
${EndIf}
|
|
FunctionEnd
|
|
|
|
Function un.onInit
|
|
!insertmacro SetContext
|
|
|
|
!if "${INSTALLMODE}" == "both"
|
|
!insertmacro MULTIUSER_UNINIT
|
|
!endif
|
|
|
|
!insertmacro MUI_UNGETLANGUAGE
|
|
|
|
${GetOptions} $CMDLINE "/P" $PassiveMode
|
|
${IfNot} ${Errors}
|
|
StrCpy $PassiveMode 1
|
|
${EndIf}
|
|
|
|
${GetOptions} $CMDLINE "/UPDATE" $UpdateMode
|
|
${IfNot} ${Errors}
|
|
StrCpy $UpdateMode 1
|
|
${EndIf}
|
|
FunctionEnd
|
|
|
|
Section Uninstall
|
|
|
|
!ifmacrodef NSIS_HOOK_PREUNINSTALL
|
|
!insertmacro NSIS_HOOK_PREUNINSTALL
|
|
!endif
|
|
|
|
!insertmacro CheckIfAppIsRunning "${MAINBINARYNAME}.exe" "${PRODUCTNAME}"
|
|
!insertmacro CheckAllVergeProcesses
|
|
!insertmacro RemoveVergeService
|
|
|
|
; Remove cached window state files
|
|
DetailPrint "Removing window-state.json / .window-state.json"
|
|
SetShellVarContext current
|
|
Delete "$APPDATA\io.github.clash-verge-rev.clash-verge-rev\window-state.json"
|
|
Delete "$APPDATA\io.github.clash-verge-rev.clash-verge-rev\.window-state.json"
|
|
|
|
; Clean legacy auto-launch registry entries
|
|
StrCpy $R1 "Software\Microsoft\Windows\CurrentVersion\Run"
|
|
|
|
SetRegView 64
|
|
ReadRegStr $R2 HKCU "$R1" "Clash Verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKCU "$R1" "Clash Verge"
|
|
${EndIf}
|
|
ReadRegStr $R2 HKLM "$R1" "Clash Verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKLM "$R1" "Clash Verge"
|
|
${EndIf}
|
|
ReadRegStr $R2 HKCU "$R1" "clash-verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKCU "$R1" "clash-verge"
|
|
${EndIf}
|
|
ReadRegStr $R2 HKLM "$R1" "clash-verge"
|
|
${If} $R2 != ""
|
|
DeleteRegValue HKLM "$R1" "clash-verge"
|
|
${EndIf}
|
|
|
|
; Remove legacy executables
|
|
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
|
|
Delete "$INSTDIR\Clash Verge.exe"
|
|
|
|
!insertmacro SetContext
|
|
|
|
; Delete the app directory and its content from disk
|
|
; Copy main executable
|
|
Delete "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
|
|
; Delete resources
|
|
{{#each resources}}
|
|
Delete "$INSTDIR\\{{this.[1]}}"
|
|
{{/each}}
|
|
|
|
; Delete external binaries
|
|
{{#each binaries}}
|
|
Delete "$INSTDIR\\{{this}}"
|
|
{{/each}}
|
|
|
|
; A failed core upgrade leaves the displaced binary behind; it is never user data.
|
|
Delete "$INSTDIR\verge-mihomo.old"
|
|
Delete "$INSTDIR\verge-mihomo-alpha.old"
|
|
|
|
; Delete app associations
|
|
{{#each file_associations as |association| ~}}
|
|
{{#each association.ext as |ext| ~}}
|
|
!insertmacro APP_UNASSOCIATE "{{ext}}" "{{or association.name ext}}"
|
|
{{/each}}
|
|
{{/each}}
|
|
|
|
; Delete deep links
|
|
{{#each deep_link_protocols as |protocol| ~}}
|
|
ReadRegStr $R7 SHCTX "Software\Classes\\{{protocol}}\shell\open\command" ""
|
|
${If} $R7 == "$\"$INSTDIR\${MAINBINARYNAME}.exe$\" $\"%1$\""
|
|
DeleteRegKey SHCTX "Software\Classes\\{{protocol}}"
|
|
${EndIf}
|
|
{{/each}}
|
|
|
|
|
|
; Delete uninstaller
|
|
Delete "$INSTDIR\uninstall.exe"
|
|
|
|
{{#each resources_ancestors}}
|
|
RMDir /REBOOTOK "$INSTDIR\\{{this}}"
|
|
{{/each}}
|
|
RMDir "$INSTDIR"
|
|
|
|
; Remove shortcuts if not updating
|
|
${If} $UpdateMode <> 1
|
|
!insertmacro DeleteAppUserModelId
|
|
|
|
; Remove start menu shortcut
|
|
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
|
|
!insertmacro IsShortcutTarget "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
Pop $0
|
|
${If} $0 = 1
|
|
!insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk"
|
|
Delete "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk"
|
|
RMDir "$SMPROGRAMS\$AppStartMenuFolder"
|
|
${EndIf}
|
|
!insertmacro IsShortcutTarget "$SMPROGRAMS\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
Pop $0
|
|
${If} $0 = 1
|
|
!insertmacro UnpinShortcut "$SMPROGRAMS\${PRODUCTNAME}.lnk"
|
|
Delete "$SMPROGRAMS\${PRODUCTNAME}.lnk"
|
|
${EndIf}
|
|
|
|
; Remove desktop shortcuts
|
|
!insertmacro IsShortcutTarget "$DESKTOP\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
Pop $0
|
|
${If} $0 = 1
|
|
!insertmacro UnpinShortcut "$DESKTOP\${PRODUCTNAME}.lnk"
|
|
Delete "$DESKTOP\${PRODUCTNAME}.lnk"
|
|
${EndIf}
|
|
|
|
; Remove legacy public desktop shortcuts
|
|
Delete "C:\Users\Public\Desktop\Clash Verge.lnk"
|
|
Delete "C:\Users\Public\Desktop\clash-verge.lnk"
|
|
|
|
; Remove legacy shortcuts from all user desktops
|
|
DetailPrint "Removing ${PRODUCTNAME} shortcuts from all user desktops..."
|
|
SetRegView 64
|
|
StrCpy $R1 0
|
|
LegacyUserLoop:
|
|
EnumRegKey $R2 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $R1
|
|
${If} $R2 == ""
|
|
Goto LegacyUserDone
|
|
${EndIf}
|
|
ReadRegStr $R3 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$R2" "ProfileImagePath"
|
|
${If} $R3 != ""
|
|
StrCpy $R4 "$R3\Desktop"
|
|
Delete "$R4\Clash Verge.lnk"
|
|
Delete "$R4\clash-verge.lnk"
|
|
${EndIf}
|
|
IntOp $R1 $R1 + 1
|
|
Goto LegacyUserLoop
|
|
LegacyUserDone:
|
|
!insertmacro SetContext
|
|
|
|
; Remove legacy start menu folders
|
|
SetShellVarContext current
|
|
RMDir /r /REBOOTOK "$SMPROGRAMS\Clash Verge"
|
|
RMDir /r /REBOOTOK "$SMPROGRAMS\clash-verge"
|
|
!insertmacro SetContext
|
|
RMDir /r /REBOOTOK "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Clash Verge"
|
|
RMDir /r /REBOOTOK "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\clash-verge"
|
|
|
|
; Clean legacy registry keys
|
|
SetRegView 64
|
|
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Clash Verge.exe"
|
|
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\clash-verge.exe"
|
|
DeleteRegKey HKLM "Software\Clash Verge Rev"
|
|
DeleteRegKey HKLM "Software\Clash Verge"
|
|
DeleteRegKey HKCU "Software\Clash Verge Rev"
|
|
DeleteRegKey HKCU "Software\Clash Verge"
|
|
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\ClashVerge"
|
|
DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Clash Verge"
|
|
|
|
StrCpy $R1 0
|
|
LegacyUninstallLoop:
|
|
EnumRegKey $R2 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" $R1
|
|
${If} $R2 == ""
|
|
Goto LegacyUninstallDone
|
|
${EndIf}
|
|
ReadRegStr $R3 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R2" "DisplayName"
|
|
${If} $R3 != ""
|
|
StrCmp $R3 "Clash Verge" 0 +3
|
|
StrCmp $R3 "clash-verge" 0 +2
|
|
DeleteRegKey HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$R2"
|
|
${EndIf}
|
|
IntOp $R1 $R1 + 1
|
|
Goto LegacyUninstallLoop
|
|
LegacyUninstallDone:
|
|
!insertmacro SetContext
|
|
${EndIf}
|
|
|
|
; Remove registry information for add/remove programs
|
|
!if "${INSTALLMODE}" == "both"
|
|
DeleteRegKey SHCTX "${UNINSTKEY}"
|
|
!else if "${INSTALLMODE}" == "perMachine"
|
|
DeleteRegKey HKLM "${UNINSTKEY}"
|
|
!else
|
|
DeleteRegKey HKCU "${UNINSTKEY}"
|
|
!endif
|
|
|
|
; Removes the Autostart entry for ${PRODUCTNAME} from the HKCU Run key if it exists.
|
|
; This ensures the program does not launch automatically after uninstallation if it exists.
|
|
; If it doesn't exist, it does nothing.
|
|
; We do this when not updating (to preserve the registry value on updates)
|
|
${If} $UpdateMode <> 1
|
|
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "${PRODUCTNAME}"
|
|
${EndIf}
|
|
|
|
; Delete app data if the checkbox is selected
|
|
; and if not updating
|
|
${If} $DeleteAppDataCheckboxState = 1
|
|
${AndIf} $UpdateMode <> 1
|
|
; Clear the install location $INSTDIR from registry
|
|
DeleteRegKey SHCTX "${MANUPRODUCTKEY}"
|
|
DeleteRegKey /ifempty SHCTX "${MANUKEY}"
|
|
|
|
; Clear the install language from registry
|
|
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
|
|
DeleteRegKey /ifempty HKCU "${MANUPRODUCTKEY}"
|
|
DeleteRegKey /ifempty HKCU "${MANUKEY}"
|
|
|
|
SetShellVarContext current
|
|
RmDir /r "$APPDATA\${BUNDLEID}"
|
|
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
|
|
${EndIf}
|
|
|
|
!ifmacrodef NSIS_HOOK_POSTUNINSTALL
|
|
!insertmacro NSIS_HOOK_POSTUNINSTALL
|
|
!endif
|
|
|
|
; Auto close if passive mode or updating
|
|
${If} $PassiveMode = 1
|
|
${OrIf} $UpdateMode = 1
|
|
SetAutoClose true
|
|
${EndIf}
|
|
SectionEnd
|
|
|
|
Function RestorePreviousInstallLocation
|
|
ReadRegStr $4 SHCTX "${MANUPRODUCTKEY}" ""
|
|
StrCmp $4 "" +2 0
|
|
StrCpy $INSTDIR $4
|
|
FunctionEnd
|
|
|
|
Function Skip
|
|
Abort
|
|
FunctionEnd
|
|
|
|
Function SkipIfPassive
|
|
${IfThen} $PassiveMode = 1 ${|} Abort ${|}
|
|
FunctionEnd
|
|
Function un.SkipIfPassive
|
|
${IfThen} $PassiveMode = 1 ${|} Abort ${|}
|
|
FunctionEnd
|
|
|
|
Function CreateOrUpdateStartMenuShortcut
|
|
; We used to use product name as MAINBINARYNAME
|
|
; migrate old shortcuts to target the new MAINBINARYNAME
|
|
StrCpy $R0 0
|
|
|
|
!insertmacro IsShortcutTarget "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk" "$INSTDIR\$OldMainBinaryName"
|
|
Pop $0
|
|
${If} $0 = 1
|
|
!insertmacro SetShortcutTarget "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
StrCpy $R0 1
|
|
${EndIf}
|
|
|
|
!insertmacro IsShortcutTarget "$SMPROGRAMS\${PRODUCTNAME}.lnk" "$INSTDIR\$OldMainBinaryName"
|
|
Pop $0
|
|
${If} $0 = 1
|
|
!insertmacro SetShortcutTarget "$SMPROGRAMS\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
StrCpy $R0 1
|
|
${EndIf}
|
|
|
|
${If} $R0 = 1
|
|
Return
|
|
${EndIf}
|
|
|
|
; Skip creating shortcut if in update mode or no shortcut mode
|
|
; but always create if migrating from wix
|
|
${If} $WixMode = 0
|
|
${If} $UpdateMode = 1
|
|
${OrIf} $NoShortcutMode = 1
|
|
Return
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
!if "${STARTMENUFOLDER}" != ""
|
|
CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder"
|
|
CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
!insertmacro SetLnkAppUserModelId "$SMPROGRAMS\$AppStartMenuFolder\${PRODUCTNAME}.lnk"
|
|
!else
|
|
CreateShortcut "$SMPROGRAMS\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
!insertmacro SetLnkAppUserModelId "$SMPROGRAMS\${PRODUCTNAME}.lnk"
|
|
!endif
|
|
FunctionEnd
|
|
|
|
Function CreateOrUpdateDesktopShortcut
|
|
; We used to use product name as MAINBINARYNAME
|
|
; migrate old shortcuts to target the new MAINBINARYNAME
|
|
!insertmacro IsShortcutTarget "$DESKTOP\${PRODUCTNAME}.lnk" "$INSTDIR\$OldMainBinaryName"
|
|
Pop $0
|
|
${If} $0 = 1
|
|
!insertmacro SetShortcutTarget "$DESKTOP\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
Return
|
|
${EndIf}
|
|
|
|
; Skip creating shortcut if in update mode or no shortcut mode
|
|
; but always create if migrating from wix
|
|
${If} $WixMode = 0
|
|
${If} $UpdateMode = 1
|
|
${OrIf} $NoShortcutMode = 1
|
|
Return
|
|
${EndIf}
|
|
${EndIf}
|
|
|
|
CreateShortcut "$DESKTOP\${PRODUCTNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
|
|
!insertmacro SetLnkAppUserModelId "$DESKTOP\${PRODUCTNAME}.lnk"
|
|
FunctionEnd
|