From b8fd8e1baceb1bd43c3e065b43de496da515dc60 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 30 Apr 2024 15:11:00 +0300 Subject: [PATCH] refactor(nsis): use nsis's built-in com plugin instead of ApplicationID plugin (#9606) * Use WinShell instead of ApplicationID * Uninst shortcut before removing start menu one * Use nsis's buit-in com plugin instead of WinShell * Remove download ApplicationID code * Add change file * Clippy and format * Allow dead code on extract_zip * Qualify extract_zip path to make clippy happy * Move macro up Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com> --- .changes/drop-nsis-applicationid.md | 5 ++ tooling/bundler/src/bundle/windows/nsis.rs | 25 ++----- .../bundle/windows/templates/installer.nsi | 65 ++++++++++++++++++- tooling/bundler/src/bundle/windows/util.rs | 3 +- 4 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 .changes/drop-nsis-applicationid.md diff --git a/.changes/drop-nsis-applicationid.md b/.changes/drop-nsis-applicationid.md new file mode 100644 index 00000000000..55be7e96ca1 --- /dev/null +++ b/.changes/drop-nsis-applicationid.md @@ -0,0 +1,5 @@ +--- +'tauri-bundler': 'patch:enhance' +--- + +Use nsis's built-in COM plugin instead of `ApplicationID` plugin, this reduces the installer size by 100 KB, and also fixes pinned shortcut not getting cleaned up on uninstall. diff --git a/tooling/bundler/src/bundle/windows/nsis.rs b/tooling/bundler/src/bundle/windows/nsis.rs index d91e9a1f0df..32d64fa2f3f 100644 --- a/tooling/bundler/src/bundle/windows/nsis.rs +++ b/tooling/bundler/src/bundle/windows/nsis.rs @@ -8,9 +8,8 @@ use crate::{ bundle::{ common::CommandExt, windows::util::{ - download, download_and_verify, download_webview2_bootstrapper, - download_webview2_offline_installer, extract_zip, verify_file_hash, HashAlgorithm, - NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, + download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer, + verify_file_hash, HashAlgorithm, NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME, }, }, Settings, @@ -24,7 +23,7 @@ use tauri_utils::config::{NSISInstallerMode, NsisCompression, WebviewInstallMode use std::{ collections::{BTreeMap, HashMap}, - fs::{copy, create_dir_all, remove_dir_all, rename, write}, + fs::{create_dir_all, remove_dir_all, rename, write}, path::{Path, PathBuf}, process::Command, }; @@ -35,7 +34,6 @@ const NSIS_URL: &str = "https://github.com/tauri-apps/binary-releases/releases/download/nsis-3/nsis-3.zip"; #[cfg(target_os = "windows")] const NSIS_SHA1: &str = "057e83c7d82462ec394af76c87d06733605543d4"; -const NSIS_APPLICATIONID_URL: &str = "https://github.com/tauri-apps/binary-releases/releases/download/nsis-plugins-v0/NSIS-ApplicationID.zip"; const NSIS_TAURI_UTILS_URL: &str = "https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.3.0/nsis_tauri_utils.dll"; const NSIS_TAURI_UTILS_SHA1: &str = "01E48D6429B48B640230C6CE8F257C84758943AA"; @@ -105,26 +103,13 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c #[cfg(target_os = "windows")] { let data = download_and_verify(NSIS_URL, NSIS_SHA1, HashAlgorithm::Sha1)?; - info!("extracting NSIS"); - extract_zip(&data, _tauri_tools_path)?; + log::info!("extracting NSIS"); + crate::bundle::windows::util::extract_zip(&data, _tauri_tools_path)?; rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?; } let nsis_plugins = nsis_toolset_path.join("Plugins"); - let data = download(NSIS_APPLICATIONID_URL)?; - info!("extracting NSIS ApplicationID plugin"); - extract_zip(&data, &nsis_plugins)?; - - create_dir_all(nsis_plugins.join("x86-unicode"))?; - - copy( - nsis_plugins - .join("ReleaseUnicode") - .join("ApplicationID.dll"), - nsis_plugins.join("x86-unicode").join("ApplicationID.dll"), - )?; - let data = download_and_verify( NSIS_TAURI_UTILS_URL, NSIS_TAURI_UTILS_SHA1, diff --git a/tooling/bundler/src/bundle/windows/templates/installer.nsi b/tooling/bundler/src/bundle/windows/templates/installer.nsi index 22be926a969..f6e6bfc36bc 100644 --- a/tooling/bundler/src/bundle/windows/templates/installer.nsi +++ b/tooling/bundler/src/bundle/windows/templates/installer.nsi @@ -11,6 +11,8 @@ Unicode true !include x64.nsh !include WordFunc.nsh !include "StrFunc.nsh" +!include "Win\COM.nsh" +!include "Win\Propkey.nsh" ${StrCase} ${StrLoc} @@ -621,6 +623,35 @@ Function un.onInit !insertmacro MUI_UNGETLANGUAGE 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 !insertmacro CheckIfAppIsRunning @@ -646,6 +677,10 @@ Section Uninstall {{/each}} RMDir "$INSTDIR" + !insertmacro DeleteAppUserModelId + !insertmacro UnpinShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" + !insertmacro UnpinShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" + ; Remove start menu shortcut !insertmacro MUI_STARTMENU_GETFOLDER Application $AppStartMenuFolder Delete "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" @@ -687,13 +722,39 @@ Function SkipIfPassive ${IfThen} $PassiveMode == 1 ${|} Abort ${|} 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 CreateShortcut "$DESKTOP\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" - ApplicationID::Set "$DESKTOP\${MAINBINARYNAME}.lnk" "${BUNDLEID}" + !insertmacro SetLnkAppUserModelId "$DESKTOP\${MAINBINARYNAME}.lnk" FunctionEnd Function CreateStartMenuShortcut CreateDirectory "$SMPROGRAMS\$AppStartMenuFolder" CreateShortcut "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "$INSTDIR\${MAINBINARYNAME}.exe" - ApplicationID::Set "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" "${BUNDLEID}" + !insertmacro SetLnkAppUserModelId "$SMPROGRAMS\$AppStartMenuFolder\${MAINBINARYNAME}.lnk" FunctionEnd diff --git a/tooling/bundler/src/bundle/windows/util.rs b/tooling/bundler/src/bundle/windows/util.rs index 6ae044db685..1541de876af 100644 --- a/tooling/bundler/src/bundle/windows/util.rs +++ b/tooling/bundler/src/bundle/windows/util.rs @@ -132,7 +132,8 @@ pub fn verify_file_hash>( verify_hash(&data, hash, hash_algorithm) } -/// Extracts the zips from memory into a useable path. +/// Extracts the zips from memory into a usable path. +#[allow(dead_code)] pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> { let cursor = Cursor::new(data);