fix(installer): make Windows desktop shortcut optional (#8601)

Add a "Create a desktop shortcut" checkbox to the MSI install-directory page and a public CREATEDESKTOPSHORTCUT property for silent installs. Upgrades keep the current state of the desktop shortcut, and clearing the option during an upgrade removes the old one.

The desktop shortcut is now copied from the Start Menu shortcut by custom actions with Return="ignore", so a Desktop that cannot be written no longer blocks or rolls back the install.

Fixes #5667

Co-authored-by: thefool <thefoolyuzhe@gmail.com>
This commit is contained in:
funkpopoandthefool authored and GitHub committed 2026-10-21 16:41:46 +08:00
1 parent ab5d4a12dd
commit cdfdff5ee7
4 files changed
+94 -18

No files matched your search

@@ -53,6 +53,14 @@ Installing with npm requires Node.js. Each tool requires a different version (fo
2. Double-click to run the installer
3. Follow the prompts to complete installation
The installation directory page includes a **Create a desktop shortcut** option. Clear it to skip desktop shortcut creation; you can still launch the app from the Start menu. It is selected by default on a fresh install. Upgrades preserve whether the desktop shortcut exists, so a deleted shortcut is not recreated automatically. You can also clear the option during an upgrade to remove an existing desktop shortcut. If the desktop shortcut cannot be created (for example, the Desktop folder is read-only or blocked by security software), the installer skips it and finishes the installation instead of failing or rolling back.
For a silent installation, pass `CREATEDESKTOPSHORTCUT=0` to skip desktop shortcut creation:
```powershell
msiexec /i "CC-Switch-v{version}-Windows.msi" /qn CREATEDESKTOPSHORTCUT=0
```
Note: If nothing happens when you run the installer, right-click the file, choose `Properties`, and on the `General` tab check `Unblock` under `Security`.
### Portable Version (No Installation Required)
@@ -53,6 +53,14 @@ npm でインストールするには、先に Node.js が必要です。必要
2. インストーラーをダブルクリックして実行
3. 指示に従ってインストール
インストール先の選択画面には **Create a desktop shortcut**(デスクトップショートカットを作成)オプションがあります。チェックを外すとデスクトップショートカットの作成をスキップでき、インストール後はスタートメニューから起動できます。新規インストールでは既定で選択されます。アップグレード時はショートカットの有無を引き継ぎ、削除済みのショートカットは自動で再作成されません。アップグレード時にチェックを外すと、既存のデスクトップショートカットも削除されます。デスクトップショートカットを作成できない場合(デスクトップが読み取り専用、セキュリティソフトによるブロックなど)は、そのショートカットを省略してインストールを続行し、中断やロールバックは行いません。
サイレントインストールでは、`CREATEDESKTOPSHORTCUT=0` を指定するとデスクトップショートカットの作成をスキップできます。
```powershell
msiexec /i "CC-Switch-v{バージョン}-Windows.msi" /qn CREATEDESKTOPSHORTCUT=0
```
注意:インストーラーを実行しても何も起こらない場合は、ファイルを右クリックしてメニューから `プロパティ` を開き、`全般` タブの `セキュリティ` で `許可する` にチェックを入れてください
### ポータブル版(インストール不要)
@@ -53,6 +53,14 @@ Claude Desktop、MiniMax Code(可用方式一安装)以及 Windows 上的安
2. 双击运行安装程序
3. 按提示完成安装
安装目录页面提供「Create a desktop shortcut」(创建桌面快捷方式)选项,取消勾选即可跳过桌面快捷方式创建,安装后仍可从开始菜单启动。首次安装默认勾选;升级时沿用桌面快捷方式是否存在的状态,已删除的快捷方式不会自动重建。升级时也可以取消勾选,移除已有的桌面快捷方式。如果桌面快捷方式无法创建(例如桌面目录只读、被安全软件拦截),安装程序会跳过它并继续完成安装,不会中断或回滚。
静默安装时,可通过 `CREATEDESKTOPSHORTCUT=0` 跳过桌面快捷方式创建:
```powershell
msiexec /i "CC-Switch-v{版本号}-Windows.msi" /qn CREATEDESKTOPSHORTCUT=0
```
注意:运行安装程序后如果没有任何反应,可以在文件上点击右键,在弹出的菜单中打开 `属性`,`常规`页中的 `安全` 勾选 `解除锁定`
### 绿色版(免安装)
+70 -18
View File
@@ -64,6 +64,12 @@
<FileSearch Id="DesktopShortcutFileSearch" Name="{{product_name}}.lnk" />
</DirectorySearch>
</Property>
<!-- Public property also allows silent installs to opt out with CREATEDESKTOPSHORTCUT=0. -->
<Property Id="CREATEDESKTOPSHORTCUT" Secure="yes" />
<!-- Initialize only once so the execute sequence does not reset an unchecked checkbox. -->
<SetProperty Id="CREATEDESKTOPSHORTCUT" Value="1" Before="CostFinalize" Sequence="first">
NOT CREATEDESKTOPSHORTCUT AND (NOT (Installed OR WIX_UPGRADE_DETECTED) OR DESKTOPSHORTCUTEXISTS)
</SetProperty>
{{#if allow_downgrades}}
<MajorUpgrade Schedule="afterInstallInitialize" AllowDowngrades="yes" />
@@ -75,6 +81,35 @@
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
</InstallExecuteSequence>
<!-- The desktop shortcut is optional, so failing to create or remove it (read-only or redirected
Desktop, security software, ...) must not abort or roll back the install (#5667). A Shortcut
table row has no way to tolerate that, so the desktop shortcut is a copy of the Start Menu
shortcut made by custom actions that ignore their own failure. Copying keeps the target,
working directory and AppUserModelID of the Start Menu shortcut. CAQuietExec reads its
command line from the property that shares the action's Id. -->
<CustomAction Id="CreateDesktopShortcut" BinaryKey="WixCA" DllEntry="CAQuietExec"
Execute="deferred" Impersonate="yes" Return="ignore" />
<SetProperty Id="CreateDesktopShortcut" Before="CreateDesktopShortcut" Sequence="execute"
Value="&quot;[SystemFolder]cmd.exe&quot; /c copy /y &quot;[ApplicationProgramsFolder]{{product_name}}.lnk&quot; &quot;[DesktopFolder]{{product_name}}.lnk&quot;">
NOT REMOVE AND CREATEDESKTOPSHORTCUT = "1"
</SetProperty>
<!-- Removed on a real uninstall, and on an upgrade where the user opted out: older installers leave
their desktop shortcut behind when they are replaced. -->
<CustomAction Id="RemoveDesktopShortcut" BinaryKey="WixCA" DllEntry="CAQuietExec"
Execute="deferred" Impersonate="yes" Return="ignore" />
<SetProperty Id="RemoveDesktopShortcut" Before="RemoveDesktopShortcut" Sequence="execute"
Value="&quot;[SystemFolder]cmd.exe&quot; /c del /f /q &quot;[DesktopFolder]{{product_name}}.lnk&quot;">
(REMOVE = "ALL" AND NOT UPGRADINGPRODUCTCODE) OR (WIX_UPGRADE_DETECTED AND DESKTOPSHORTCUTEXISTS AND NOT REMOVE AND CREATEDESKTOPSHORTCUT &lt;&gt; "1")
</SetProperty>
<InstallExecuteSequence>
<Custom Action="RemoveDesktopShortcut" After="RemoveShortcuts">
(REMOVE = "ALL" AND NOT UPGRADINGPRODUCTCODE) OR (WIX_UPGRADE_DETECTED AND DESKTOPSHORTCUTEXISTS AND NOT REMOVE AND CREATEDESKTOPSHORTCUT &lt;&gt; "1")
</Custom>
<Custom Action="CreateDesktopShortcut" After="CreateShortcuts">NOT REMOVE AND CREATEDESKTOPSHORTCUT = "1"</Custom>
</InstallExecuteSequence>
<Media Id="1" Cabinet="app.cab" EmbedCab="yes" />
{{#if banner_path}}
@@ -112,19 +147,49 @@
<CustomAction Id="LaunchApplication" Impersonate="yes" FileKey="Path" ExeCommand="[LAUNCHAPPARGS]" Return="asyncNoWait" />
<UI>
<!-- Extend the standard install-directory page with an optional desktop shortcut. -->
<Dialog Id="InstallDirOptionsDlg" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgDescription)" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.InstallDirDlgTitle)" />
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="FolderLabel" Type="Text" X="20" Y="60" Width="290" Height="30" NoPrefix="yes" Text="!(loc.InstallDirDlgFolderLabel)" />
<Control Id="Folder" Type="PathEdit" X="20" Y="100" Width="320" Height="18" Property="WIXUI_INSTALLDIR" Indirect="yes" />
<Control Id="ChangeFolder" Type="PushButton" X="20" Y="120" Width="56" Height="17" Text="!(loc.InstallDirDlgChange)" />
<Control Id="DesktopShortcut" Type="CheckBox" X="20" Y="160" Width="320" Height="18" Property="CREATEDESKTOPSHORTCUT" CheckBoxValue="1" Text="Create a &amp;desktop shortcut" />
</Dialog>
<!-- launch app checkbox -->
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="InstallDirOptionsDlg" Order="2">LicenseAccepted = "1"</Publish>
<Publish Dialog="InstallDirOptionsDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
<Publish Dialog="InstallDirOptionsDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2">NOT WIXUI_DONTVALIDATEPATH</Publish>
<Publish Dialog="InstallDirOptionsDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID <> "1"]]></Publish>
<Publish Dialog="InstallDirOptionsDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4">WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID = "1"</Publish>
<Publish Dialog="InstallDirOptionsDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1">1</Publish>
<Publish Dialog="InstallDirOptionsDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2">1</Publish>
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirOptionsDlg" Order="3">NOT Installed</Publish>
{{#if license}}
<Publish Dialog="InstallDirOptionsDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg">1</Publish>
{{/if}}
{{#unless license}}
<!-- Skip license dialog -->
<Publish Dialog="WelcomeDlg"
Control="Next"
Event="NewDialog"
Value="InstallDirDlg"
Value="InstallDirOptionsDlg"
Order="2">1</Publish>
<Publish Dialog="InstallDirDlg"
<Publish Dialog="InstallDirOptionsDlg"
Control="Back"
Event="NewDialog"
Value="WelcomeDlg"
@@ -135,21 +200,9 @@
<UIRef Id="WixUI_InstallDir" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" Name="Desktop">
<Component Id="ApplicationShortcutDesktop" Guid="*">
<!-- Fresh install: create it. Upgrade: only keep it if the user hasn't deleted it -->
<Condition><![CDATA[NOT WIX_UPGRADE_DETECTED OR DESKTOPSHORTCUTEXISTS]]></Condition>
<Shortcut Id="ApplicationDesktopShortcut"
Name="{{product_name}}"
Description="Runs {{product_name}}"
Target="[!Path]"
WorkingDirectory="INSTALLDIR">
<ShortcutProperty Key="System.AppUserModel.ID" Value="{{bundle_id}}"/>
</Shortcut>
<RemoveFolder Id="DesktopFolder" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="Desktop Shortcut" Type="integer" Value="1" KeyPath="yes" />
</Component>
</Directory>
<!-- No component: the desktop shortcut is made by the CreateDesktopShortcut custom action.
The directory keeps [DesktopFolder] available to AppSearch and to those actions. -->
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="LocalAppDataFolder">
<Directory Id="TauriLocalAppDataPrograms" Name="Programs">
<Directory Id="INSTALLDIR" Name="{{product_name}}"/>
@@ -287,7 +340,6 @@
<ComponentRef Id="Path"/>
<ComponentRef Id="CMP_UninstallShortcut" />
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="ApplicationShortcutDesktop" />
</Feature>
<Feature