feat(bundler/nsis): add /UPDATE flag (#9559)

* feat(bundler/nsis): add `/UPDATE` flag

* typo

* typo

* skip webview2 installation on updating
This commit is contained in:
Amr Bashir 2024-05-25 15:41:09 +03:00 committed by GitHub
parent 80aa504987
commit 418d72d72d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 219 additions and 175 deletions

View File

@ -0,0 +1,6 @@
---
"tauri-bundler": "patch:enhance"
---
Added `/UPDATE` flag for NSIS installer which will make the installer avoid deleting app data and re-creating shortcuts.

View File

@ -451,6 +451,10 @@ fn build_nsis_app_installer(
output_path.join("FileAssociation.nsh"), output_path.join("FileAssociation.nsh"),
include_str!("./templates/FileAssociation.nsh"), include_str!("./templates/FileAssociation.nsh"),
)?; )?;
write_ut16_le_with_bom(
output_path.join("utils.nsh"),
include_str!("./templates/utils.nsh"),
)?;
let installer_nsi_path = output_path.join("installer.nsi"); let installer_nsi_path = output_path.join("installer.nsi");
write_ut16_le_with_bom( write_ut16_le_with_bom(

View File

@ -11,6 +11,7 @@ ManifestDPIAware true
!include FileFunc.nsh !include FileFunc.nsh
!include x64.nsh !include x64.nsh
!include WordFunc.nsh !include WordFunc.nsh
!include "utils.nsh"
!include "FileAssociation.nsh" !include "FileAssociation.nsh"
!include "StrFunc.nsh" !include "StrFunc.nsh"
!include "Win\COM.nsh" !include "Win\COM.nsh"
@ -68,6 +69,7 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
!addplugindir "${PLUGINSPATH}" !addplugindir "${PLUGINSPATH}"
!endif !endif
; Uninstaller signing command
!if "${UNINSTALLERSIGNCOMMAND}" != "" !if "${UNINSTALLERSIGNCOMMAND}" != ""
!uninstfinalize '${UNINSTALLERSIGNCOMMAND}' !uninstfinalize '${UNINSTALLERSIGNCOMMAND}'
!endif !endif
@ -98,17 +100,17 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
!include MultiUser.nsh !include MultiUser.nsh
!endif !endif
; installer icon ; Installer icon
!if "${INSTALLERICON}" != "" !if "${INSTALLERICON}" != ""
!define MUI_ICON "${INSTALLERICON}" !define MUI_ICON "${INSTALLERICON}"
!endif !endif
; installer sidebar image ; Installer sidebar image
!if "${SIDEBARIMAGE}" != "" !if "${SIDEBARIMAGE}" != ""
!define MUI_WELCOMEFINISHPAGE_BITMAP "${SIDEBARIMAGE}" !define MUI_WELCOMEFINISHPAGE_BITMAP "${SIDEBARIMAGE}"
!endif !endif
; installer header image ; Installer header image
!if "${HEADERIMAGE}" != "" !if "${HEADERIMAGE}" != ""
!define MUI_HEADERIMAGE !define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_BITMAP "${HEADERIMAGE}" !define MUI_HEADERIMAGE_BITMAP "${HEADERIMAGE}"
@ -136,7 +138,6 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
!insertmacro MULTIUSER_PAGE_INSTALLMODE !insertmacro MULTIUSER_PAGE_INSTALLMODE
!endif !endif
; 4. Custom page to ask user if he wants to reinstall/uninstall ; 4. Custom page to ask user if he wants to reinstall/uninstall
; only if a previous installation was detected ; only if a previous installation was detected
Var ReinstallPageCheck Var ReinstallPageCheck
@ -217,6 +218,12 @@ Function PageReinstall
Abort Abort
${EndIf} ${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.
Call SkipIfPassive Call SkipIfPassive
nsDialogs::Create 1018 nsDialogs::Create 1018
@ -232,7 +239,7 @@ Function PageReinstall
${NSD_CreateRadioButton} 30u 70u -30u 8u $R3 ${NSD_CreateRadioButton} 30u 70u -30u 8u $R3
Pop $R3 Pop $R3
; disable this radio button if downgrading and downgrades are disabled ; Disable this radio button if downgrading and downgrades are disabled
!if "${ALLOWDOWNGRADES}" == "false" !if "${ALLOWDOWNGRADES}" == "false"
${IfThen} $R0 == -1 ${|} EnableWindow $R3 0 ${|} ${IfThen} $R0 == -1 ${|} EnableWindow $R3 0 ${|}
!endif !endif
@ -310,8 +317,8 @@ FunctionEnd
!insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_DIRECTORY
; 6. Start menu shortcut page ; 6. Start menu shortcut page
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
Var AppStartMenuFolder Var AppStartMenuFolder
!define MUI_PAGE_CUSTOMFUNCTION_PRE SkipIfPassive
!insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder !insertmacro MUI_PAGE_STARTMENU Application $AppStartMenuFolder
; 7. Installation page ; 7. Installation page
@ -337,7 +344,7 @@ Var DeleteAppDataCheckbox
Var DeleteAppDataCheckboxState Var DeleteAppDataCheckboxState
!define /ifndef WS_EX_LAYOUTRTL 0x00400000 !define /ifndef WS_EX_LAYOUTRTL 0x00400000
!define MUI_PAGE_CUSTOMFUNCTION_SHOW un.ConfirmShow !define MUI_PAGE_CUSTOMFUNCTION_SHOW un.ConfirmShow
Function un.ConfirmShow Function un.ConfirmShow ; Add add a `Delete app data` check box
FindWindow $1 "#32770" "" $HWNDPARENT ; Find inner dialog FindWindow $1 "#32770" "" $HWNDPARENT ; Find inner dialog
${If} $(^RTL) == 1 ${If} $(^RTL) == 1
System::Call 'USER32::CreateWindowEx(i${__NSD_CheckBox_EXSTYLE}|${WS_EX_LAYOUTRTL},t"${__NSD_CheckBox_CLASS}",t "$(deleteAppData)",i${__NSD_CheckBox_STYLE},i 50,i 100,i 400, i 25,i$1,i0,i0,i0)i.s' System::Call 'USER32::CreateWindowEx(i${__NSD_CheckBox_EXSTYLE}|${WS_EX_LAYOUTRTL},t"${__NSD_CheckBox_CLASS}",t "$(deleteAppData)",i${__NSD_CheckBox_STYLE},i 50,i 100,i 400, i 25,i$1,i0,i0,i0)i.s'
@ -366,30 +373,17 @@ FunctionEnd
!include "{{this}}" !include "{{this}}"
{{/each}} {{/each}}
!macro SetContext
!if "${INSTALLMODE}" == "currentUser"
SetShellVarContext current
!else if "${INSTALLMODE}" == "perMachine"
SetShellVarContext all
!endif
${If} ${RunningX64}
!if "${ARCH}" == "x64"
SetRegView 64
!else if "${ARCH}" == "arm64"
SetRegView 64
!else
SetRegView 32
!endif
${EndIf}
!macroend
Var PassiveMode Var PassiveMode
Var UpdateMode
Function .onInit Function .onInit
${GetOptions} $CMDLINE "/P" $PassiveMode ${GetOptions} $CMDLINE "/P" $PassiveMode
IfErrors +2 0 IfErrors +2 0
StrCpy $PassiveMode 1 StrCpy $PassiveMode 1
${GetOptions} $CMDLINE "/UPDATE" $UpdateMode
IfErrors +2 0
StrCpy $UpdateMode 1
!if "${DISPLAYLANGUAGESELECTOR}" == "true" !if "${DISPLAYLANGUAGESELECTOR}" == "true"
!insertmacro MUI_LANGDLL_DISPLAY !insertmacro MUI_LANGDLL_DISPLAY
!endif !endif
@ -455,91 +449,56 @@ Section WebView2
StrCmp $4 "" 0 webview2_done StrCmp $4 "" 0 webview2_done
StrCmp $5 "" 0 webview2_done StrCmp $5 "" 0 webview2_done
; Webview2 install modes ; Webview2 installation
!if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper" ;
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe" ; Skip if updating
DetailPrint "$(webview2Downloading)" ${If} $UpdateMode <> 1
NSISdl::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe" !if "${INSTALLWEBVIEW2MODE}" == "downloadBootstrapper"
Pop $0 Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
${If} $0 == 0 DetailPrint "$(webview2Downloading)"
DetailPrint "$(webview2DownloadSuccess)" nsis_tauri_utils::download "https://go.microsoft.com/fwlink/p/?LinkId=2124703" "$TEMP\MicrosoftEdgeWebview2Setup.exe"
${Else} Pop $0
DetailPrint "$(webview2DownloadError)" ${If} $0 == 0
Abort "$(webview2AbortError)" DetailPrint "$(webview2DownloadSuccess)"
${EndIf} ${Else}
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe" DetailPrint "$(webview2DownloadError)"
Goto install_webview2 Abort "$(webview2AbortError)"
!endif ${EndIf}
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Goto install_webview2
!endif
!if "${INSTALLWEBVIEW2MODE}" == "embedBootstrapper" !if "${INSTALLWEBVIEW2MODE}" == "embedBootstrapper"
Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe" Delete "$TEMP\MicrosoftEdgeWebview2Setup.exe"
File "/oname=$TEMP\MicrosoftEdgeWebview2Setup.exe" "${WEBVIEW2BOOTSTRAPPERPATH}" File "/oname=$TEMP\MicrosoftEdgeWebview2Setup.exe" "${WEBVIEW2BOOTSTRAPPERPATH}"
DetailPrint "$(installingWebview2)" DetailPrint "$(installingWebview2)"
StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe" StrCpy $6 "$TEMP\MicrosoftEdgeWebview2Setup.exe"
Goto install_webview2 Goto install_webview2
!endif !endif
!if "${INSTALLWEBVIEW2MODE}" == "offlineInstaller" !if "${INSTALLWEBVIEW2MODE}" == "offlineInstaller"
Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" Delete "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
File "/oname=$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" "${WEBVIEW2INSTALLERPATH}" File "/oname=$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" "${WEBVIEW2INSTALLERPATH}"
DetailPrint "$(installingWebview2)" DetailPrint "$(installingWebview2)"
StrCpy $6 "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe" StrCpy $6 "$TEMP\MicrosoftEdgeWebView2RuntimeInstaller.exe"
Goto install_webview2 Goto install_webview2
!endif !endif
Goto webview2_done Goto webview2_done
install_webview2: install_webview2:
DetailPrint "$(installingWebview2)" DetailPrint "$(installingWebview2)"
; $6 holds the path to the webview2 installer ; $6 holds the path to the webview2 installer
ExecWait "$6 ${WEBVIEW2INSTALLERARGS} /install" $1 ExecWait "$6 ${WEBVIEW2INSTALLERARGS} /install" $1
${If} $1 == 0 ${If} $1 == 0
DetailPrint "$(webview2InstallSuccess)" DetailPrint "$(webview2InstallSuccess)"
${Else} ${Else}
DetailPrint "$(webview2InstallError)" DetailPrint "$(webview2InstallError)"
Abort "$(webview2AbortError)" Abort "$(webview2AbortError)"
${EndIf} ${EndIf}
webview2_done: webview2_done:
SectionEnd
!macro CheckIfAppIsRunning
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "${MAINBINARYNAME}.exe"
!else
nsis_tauri_utils::FindProcess "${MAINBINARYNAME}.exe"
!endif
Pop $R0
${If} $R0 = 0
IfSilent kill 0
${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK kill IDCANCEL cancel ${|}
kill:
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "${MAINBINARYNAME}.exe"
!else
nsis_tauri_utils::KillProcess "${MAINBINARYNAME}.exe"
!endif
Pop $R0
Sleep 500
${If} $R0 = 0
Goto app_check_done
${Else}
IfSilent silent ui
silent:
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 "$(appRunning)$\n"
${EndIf}
Abort
ui:
Abort "$(failedToKillApp)"
${EndIf}
cancel:
Abort "$(appRunning)"
${EndIf} ${EndIf}
app_check_done: SectionEnd
!macroend
Section Install Section Install
SetOutPath $INSTDIR SetOutPath $INSTDIR
@ -606,16 +565,19 @@ Section Install
!insertmacro MUI_STARTMENU_WRITE_END !insertmacro MUI_STARTMENU_WRITE_END
; Create shortcuts for silent and passive installers, which ; Create shortcuts for silent and passive installers, which
; can be disabled by passing `/NS` flag ; can be disabled by passing `/NS` or `/UPDATE` flag.
; GUI installer has buttons for users to control creating them ;
; GUI installer has buttons for users to control creating them.
IfSilent check_ns_flag 0 IfSilent check_ns_flag 0
${IfThen} $PassiveMode == 1 ${|} Goto check_ns_flag ${|} ${IfThen} $PassiveMode == 1 ${|} Goto check_ns_flag ${|}
Goto shortcuts_done Goto shortcuts_done
check_ns_flag: check_ns_flag:
${GetOptions} $CMDLINE "/NS" $R0 ${GetOptions} $CMDLINE "/NS" $R0
IfErrors 0 shortcuts_done IfErrors 0 shortcuts_done
Call CreateDesktopShortcut ${If} $UpdateMode <> 1
Call CreateStartMenuShortcut Call CreateDesktopShortcut
Call CreateStartMenuShortcut
${EndIf}
shortcuts_done: shortcuts_done:
; Auto close this page for passive mode ; Auto close this page for passive mode
@ -646,36 +608,11 @@ Function un.onInit
!insertmacro MUI_UNGETLANGUAGE !insertmacro MUI_UNGETLANGUAGE
FunctionEnd FunctionEnd
!macro DeleteAppUserModelId
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_DestinationList} ${IID_ICustomDestinationList} r1 ""
${If} $1 P<> 0
${ICustomDestinationList::DeleteList} $1 '("${BUNDLEID}")'
${IUnknown::Release} $1 ""
${EndIf}
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ApplicationDestinations} ${IID_IApplicationDestinations} r1 ""
${If} $1 P<> 0
${IApplicationDestinations::SetAppID} $1 '("${BUNDLEID}")i.r0'
${If} $0 >= 0
${IApplicationDestinations::RemoveAllDestinations} $1 ''
${EndIf}
${IUnknown::Release} $1 ""
${EndIf}
!macroend
; From https://stackoverflow.com/a/42816728/16993372
!macro UnpinShortcut shortcut
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_StartMenuPin} ${IID_IStartMenuPinnedList} r0 ""
${If} $0 P<> 0
System::Call 'SHELL32::SHCreateItemFromParsingName(ws, p0, g "${IID_IShellItem}", *p0r1)' "${shortcut}"
${If} $1 P<> 0
${IStartMenuPinnedList::RemoveFromList} $0 '(r1)'
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
!macroend
Section Uninstall Section Uninstall
${GetOptions} $CMDLINE "/UPDATE" $UpdateMode
IfErrors +2 0
StrCpy $UpdateMode 1
!insertmacro CheckIfAppIsRunning !insertmacro CheckIfAppIsRunning
; Delete the app directory and its content from disk ; Delete the app directory and its content from disk
@ -716,17 +653,20 @@ Section Uninstall
{{/each}} {{/each}}
RMDir "$INSTDIR" RMDir "$INSTDIR"
!insertmacro DeleteAppUserModelId ; Remove shortcuts if not updating
!insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" ${If} $UpdateMode <> 1
!insertmacro UnpinShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" !insertmacro DeleteAppUserModelId
; Remove start menu shortcut ; Remove start menu shortcut
!insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder !insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder
Delete "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" !insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk"
RMDir "$SMPROGRAMS\$AppStartMenuFolder" Delete "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk"
RMDir "$SMPROGRAMS\$AppStartMenuFolder"
; Remove desktop shortcuts ; Remove desktop shortcuts
Delete "$DESKTOP\${MAINBINARYNAME}.lnk" !insertmacro UnpinShortcut "$DESKTOP\${MAINBINARYNAME}.lnk"
Delete "$DESKTOP\${MAINBINARYNAME}.lnk"
${EndIf}
; Remove registry information for add/remove programs ; Remove registry information for add/remove programs
!if "${INSTALLMODE}" == "both" !if "${INSTALLMODE}" == "both"
@ -739,8 +679,10 @@ Section Uninstall
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language" DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
; Delete app data ; Delete app data if the checkbox is selected
${If} $DeleteAppDataCheckboxState == 1 ; and if not updating
${If} $DeleteAppDataCheckboxState = 1
${AndIf} $UpdateMode <> 1
SetShellVarContext current SetShellVarContext current
RmDir /r "$APPDATA\${BUNDLEID}" RmDir /r "$APPDATA\${BUNDLEID}"
RmDir /r "$LOCALAPPDATA\${BUNDLEID}" RmDir /r "$LOCALAPPDATA\${BUNDLEID}"
@ -761,32 +703,6 @@ Function SkipIfPassive
${IfThen} $PassiveMode == 1 ${|} Abort ${|} ${IfThen} $PassiveMode == 1 ${|} Abort ${|}
FunctionEnd FunctionEnd
!macro SetLnkAppUserModelId shortcut
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 ""
${If} $0 P<> 0
${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)'
${If} $1 P<> 0
${IPersistFile::Load} $1 '("${shortcut}", ${STGM_READWRITE})'
${IUnknown::QueryInterface} $0 '("${IID_IPropertyStore}",.r2)'
${If} $2 P<> 0
System::Call 'Oleaut32::SysAllocString(w "${BUNDLEID}") i.r3'
System::Call '*${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_ID})p.r4'
System::Call '*${SYSSTRUCT_PROPVARIANT}(${VT_BSTR},,&i4 $3)p.r5'
${IPropertyStore::SetValue} $2 '($4,$5)'
System::Call 'Oleaut32::SysFreeString($3)'
System::Free $4
System::Free $5
${IPropertyStore::Commit} $2 ""
${IUnknown::Release} $2 ""
${IPersistFile::Save} $1 '("${shortcut}",1)'
${EndIf}
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
!macroend
Function CreateDesktopShortcut Function CreateDesktopShortcut
CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe"
!insertmacro SetLnkAppUserModelId "$DESKTOP\${MAINBINARYNAME}.lnk" !insertmacro SetLnkAppUserModelId "$DESKTOP\${MAINBINARYNAME}.lnk"

View File

@ -0,0 +1,118 @@
; Change shell and registry context based on running
; architecture and chosen install mode.
!macro SetContext
!if "${INSTALLMODE}" == "currentUser"
SetShellVarContext current
!else if "${INSTALLMODE}" == "perMachine"
SetShellVarContext all
!endif
${If} ${RunningX64}
!if "${ARCH}" == "x64"
SetRegView 64
!else if "${ARCH}" == "arm64"
SetRegView 64
!else
SetRegView 32
!endif
${EndIf}
!macroend
; Checks whether app is running or not and prompts to kill it.
!macro CheckIfAppIsRunning
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::FindProcessCurrentUser "${MAINBINARYNAME}.exe"
!else
nsis_tauri_utils::FindProcess "${MAINBINARYNAME}.exe"
!endif
Pop $R0
${If} $R0 = 0
IfSilent kill 0
${IfThen} $PassiveMode != 1 ${|} MessageBox MB_OKCANCEL "$(appRunningOkKill)" IDOK kill IDCANCEL cancel ${|}
kill:
!if "${INSTALLMODE}" == "currentUser"
nsis_tauri_utils::KillProcessCurrentUser "${MAINBINARYNAME}.exe"
!else
nsis_tauri_utils::KillProcess "${MAINBINARYNAME}.exe"
!endif
Pop $R0
Sleep 500
${If} $R0 = 0
Goto app_check_done
${Else}
IfSilent silent ui
silent:
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 "$(appRunning)$\n"
${EndIf}
Abort
ui:
Abort "$(failedToKillApp)"
${EndIf}
cancel:
Abort "$(appRunning)"
${EndIf}
app_check_done:
!macroend
; Sets AppUserModelId on a shortcut
!macro SetLnkAppUserModelId shortcut
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ShellLink} ${IID_IShellLink} r0 ""
${If} $0 P<> 0
${IUnknown::QueryInterface} $0 '("${IID_IPersistFile}",.r1)'
${If} $1 P<> 0
${IPersistFile::Load} $1 '("${shortcut}", ${STGM_READWRITE})'
${IUnknown::QueryInterface} $0 '("${IID_IPropertyStore}",.r2)'
${If} $2 P<> 0
System::Call 'Oleaut32::SysAllocString(w "${BUNDLEID}") i.r3'
System::Call '*${SYSSTRUCT_PROPERTYKEY}(${PKEY_AppUserModel_ID})p.r4'
System::Call '*${SYSSTRUCT_PROPVARIANT}(${VT_BSTR},,&i4 $3)p.r5'
${IPropertyStore::SetValue} $2 '($4,$5)'
System::Call 'Oleaut32::SysFreeString($3)'
System::Free $4
System::Free $5
${IPropertyStore::Commit} $2 ""
${IUnknown::Release} $2 ""
${IPersistFile::Save} $1 '("${shortcut}",1)'
${EndIf}
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
!macroend
; Deletes jump list entries and recent destinations
!macro DeleteAppUserModelId
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_DestinationList} ${IID_ICustomDestinationList} r1 ""
${If} $1 P<> 0
${ICustomDestinationList::DeleteList} $1 '("${BUNDLEID}")'
${IUnknown::Release} $1 ""
${EndIf}
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_ApplicationDestinations} ${IID_IApplicationDestinations} r1 ""
${If} $1 P<> 0
${IApplicationDestinations::SetAppID} $1 '("${BUNDLEID}")i.r0'
${If} $0 >= 0
${IApplicationDestinations::RemoveAllDestinations} $1 ''
${EndIf}
${IUnknown::Release} $1 ""
${EndIf}
!macroend
; Unpins a shortcut from Start menu and Taskbar
;
; From https://stackoverflow.com/a/42816728/16993372
!macro UnpinShortcut shortcut
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_StartMenuPin} ${IID_IStartMenuPinnedList} r0 ""
${If} $0 P<> 0
System::Call 'SHELL32::SHCreateItemFromParsingName(ws, p0, g "${IID_IShellItem}", *p0r1)' "${shortcut}"
${If} $1 P<> 0
${IStartMenuPinnedList::RemoveFromList} $0 '(r1)'
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
!macroend