mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
fix(windows): stop in-app updates from rebooting the machine (#8628)
The MSI now carries REBOOT=ReallySuppress, so Windows Installer never restarts the machine when a file is in use during an in-app update, including updates driven by older updaters. The updater also passes REBOOT=ReallySuppress, a leftover instance is closed on the updater path, and the release workflow fails if the built MSI lacks the property. Fixes #8534
This commit is contained in:
1 parent
c776fd0ebf
commit
1ef08d5e7d
3 files changed
+44
-3
No files matched your search
@@ -230,6 +230,20 @@ jobs:
|
||||
$msi = Get-ChildItem -Path (Join-Path $targetRoot 'bundle') -Recurse -Include *.msi -ErrorAction SilentlyContinue | Select-Object -First 1
|
||||
}
|
||||
if ($null -ne $msi) {
|
||||
# #8534:旧版本的应用内更新器以 `msiexec /passive /promptrestart` 安装、不带任何 REBOOT
|
||||
# 选项,文件被占用时会不经提示直接重启系统,所以 MSI 自身必须带 REBOOT=ReallySuppress。
|
||||
$installer = New-Object -ComObject WindowsInstaller.Installer
|
||||
$db = $installer.GetType().InvokeMember('OpenDatabase', 'InvokeMethod', $null, $installer, @($msi.FullName, 0))
|
||||
$view = $db.GetType().InvokeMember('OpenView', 'InvokeMethod', $null, $db, @('SELECT `Value` FROM `Property` WHERE `Property`=''REBOOT'''))
|
||||
$view.GetType().InvokeMember('Execute', 'InvokeMethod', $null, $view, $null) | Out-Null
|
||||
$record = $view.GetType().InvokeMember('Fetch', 'InvokeMethod', $null, $view, $null)
|
||||
$reboot = if ($null -ne $record) { $record.GetType().InvokeMember('StringData', 'GetProperty', $null, $record, 1) } else { $null }
|
||||
$view.GetType().InvokeMember('Close', 'InvokeMethod', $null, $view, $null) | Out-Null
|
||||
if ($reboot -ne 'ReallySuppress') {
|
||||
throw "MSI Property table must set REBOOT=ReallySuppress (found: '$reboot') - see #8534"
|
||||
}
|
||||
Write-Host "MSI REBOOT property: $reboot"
|
||||
|
||||
$dest = "CC-Switch-$VERSION-Windows$assetSuffix.msi"
|
||||
Copy-Item $msi.FullName (Join-Path release-assets $dest)
|
||||
Write-Host "Installer copied: $dest"
|
||||
|
||||
@@ -275,8 +275,15 @@ pub async fn restart_app(app: AppHandle) -> Result<bool, String> {
|
||||
/// 这里把退出清理、安装和重启串在同一个后端流程中,避免依赖旧前端继续执行。
|
||||
#[tauri::command]
|
||||
pub async fn install_update_and_restart(app: AppHandle) -> Result<bool, String> {
|
||||
let updater = app
|
||||
.updater_builder()
|
||||
let builder = app.updater_builder();
|
||||
// Windows 上 updater 以 `msiexec /passive /promptrestart` 安装 MSI。passive 没有模态框、
|
||||
// 未指定重启选项时,安装目录里有文件被占用,Windows Installer 会不经确认直接重启系统
|
||||
// (#8534)。`/promptrestart` 只对应 REBOOTPROMPT,挡不住这一点,因此显式追加
|
||||
// REBOOT=ReallySuppress(即 `/norestart`)。旧版本的 updater 不带这个参数,
|
||||
// 由 MSI 模板自身的 REBOOT 属性兜底。
|
||||
#[cfg(target_os = "windows")]
|
||||
let builder = builder.installer_arg("REBOOT=ReallySuppress");
|
||||
let updater = builder
|
||||
.build()
|
||||
.map_err(|e| format!("初始化更新器失败: {e}"))?;
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
<?error Unsupported value of sys.BUILDARCH=$(sys.BUILDARCH)?>
|
||||
<?endif?>
|
||||
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
||||
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
|
||||
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
|
||||
<Product
|
||||
Id="*"
|
||||
Name="{{product_name}}"
|
||||
@@ -33,11 +34,30 @@
|
||||
<!-- reinstall all files; rewrite all registry entries; reinstall all shortcuts -->
|
||||
<Property Id="REINSTALLMODE" Value="amus" />
|
||||
|
||||
<!-- Never let this installer restart the machine (#8534). The in-app updater runs
|
||||
`msiexec /passive /promptrestart`; with no restart option, passive mode reboots without any
|
||||
prompt as soon as a file is still in use. Updaters shipped in older releases do not pass
|
||||
REBOOT=ReallySuppress themselves, so the package has to carry it. Files that are still
|
||||
in use are then replaced on the next restart instead. -->
|
||||
<Property Id="REBOOT" Value="ReallySuppress" />
|
||||
|
||||
<!-- Auto launch app after installation, useful for passive mode which usually used in updates -->
|
||||
<Property Id="AUTOLAUNCHAPP" Secure="yes" />
|
||||
<!-- Property to forward cli args to the launched app to not lose those of the pre-update instance -->
|
||||
<Property Id="LAUNCHAPPARGS" Secure="yes" />
|
||||
|
||||
<!-- Keep a running instance from holding the files this install replaces. AUTOLAUNCHAPP is only
|
||||
passed by the in-app updater, which has already run the app's exit cleanup, so a leftover
|
||||
instance is terminated there. A manual install only gets a polite close request and never
|
||||
kills an instance the user may still be using.
|
||||
Newer tauri-plugin-updater versions only pass AUTOLAUNCHAPP while restart_after_install is
|
||||
enabled (the default). If that is ever turned off, an update install falls back to the polite
|
||||
close request and nothing is terminated. -->
|
||||
<util:CloseApplication Id="TerminateAppForUpdate" Target="cc-switch.exe"
|
||||
CloseMessage="no" TerminateProcess="0" RebootPrompt="no">AUTOLAUNCHAPP</util:CloseApplication>
|
||||
<util:CloseApplication Id="CloseAppForInstall" Target="cc-switch.exe"
|
||||
CloseMessage="yes" RebootPrompt="no">NOT AUTOLAUNCHAPP</util:CloseApplication>
|
||||
|
||||
<!-- Whether the desktop shortcut is still there before this install runs (AppSearch runs before the old version is removed) -->
|
||||
<Property Id="DESKTOPSHORTCUTEXISTS">
|
||||
<DirectorySearch Id="DesktopShortcutDirSearch" Path="[DesktopFolder]" Depth="0">
|
||||
|
||||
Reference in new issue
Block a user