如何用PowerShell脚本静默更新Remote Desktop且不退出会话?
问题:Remote Desktop Client更新时会话强制退出,如何实现无中断静默更新?
更新Remote Desktop Client时,运行自定义脚本会导致当前Remote Desktop会话直接退出。需要实现静默更新——在不关闭正在运行的Remote Desktop会话的前提下完成更新。以下是我使用的脚本:
# 参数定义 $AppName = "Remote Desktop" $InstallerUrl = "https://go.microsoft.com/fwlink/?linkid=2068602" $InstallerPath = "$env:TEMP\RemoteDesktopClientInstaller.msi" $MSIProductName = "Remote Desktop" $DetectionStatus = "" $ExitCode = 0 # 辅助函数:获取已安装版本 function Get-InstalledVersion { $Product = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -eq $MSIProductName } if ($Product) { return $Product.Version } else { return $null } } # 辅助函数:通过计划任务调用BurntToast向用户发送通知(支持SYSTEM权限执行) function Invoke-UserToastNotification { param( [string]$Title = "Remote Desktop已更新", [string]$Body = "Remote Desktop应用已完成更新,请重启应用以使用最新版本。" ) # 获取控制台会话中的活跃用户 $activeSession = (Get-CimInstance -ClassName Win32_ComputerSystem | Select-Object -ExpandProperty UserName) if (-not $activeSession) { # 备选方案:查询会话1(通常为控制台会话) $Session = (quser | Select-String ">" | ForEach-Object { $_.ToString().Split( " ", [System.StringSplitOptions]::RemoveEmptyEntries)[0] } )[0] $ActiveSession = $Session } if (-not $activeSession) { return } $ToastScript = "$env:ProgramData\show-mrdc-toast.ps1" @" if (-not (Get-Module -ListAvailable -Name BurntToast)) { try {# 安装BurntToast模块 Install-Module -Name BurntToast -Force -Scope CurrentUser -AllowClobber -ErrorAction Stop } catch {} } try {# 导入模块到当前会话 Import-Module BurntToast -ErrorAction Stop } catch {} New-BurntToastNotification -Text '$Title', '$Body' "@ | Set-Content -Path $ToastScript -Encoding UTF8 $Action = New-ScheduledTaskAction -Execute "powershell.exe" ` -Argument "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$ToastScript`"" $Trigger = New-ScheduledTaskTrigger -AtLogOn $Principal = New-ScheduledTaskPrincipal ` -UserId $activeSession ` -LogonType Interactive ` -RunLevel Limited $TaskName = "Show-MSRemoteDesktop-Update-Toast" try {# 清理旧任务 Unregister-ScheduledTask ` -TaskName $TaskName ` -Confirm:$false ` -ErrorAction SilentlyContinue } catch {} Register-ScheduledTask ` -Action $Action ` -Trigger $Trigger ` -Principal $Principal ` -TaskName $TaskName | Out-Null Start-ScheduledTask -TaskName $TaskName Start-Sleep -Seconds 10 Remove-Item $ToastScript -Force -ErrorAction SilentlyContinue Unregister-ScheduledTask ` -TaskName $TaskName ` -Confirm:$false ` -ErrorAction SilentlyContinue } # 可选:设置为$true并定义$LatestVersion以启用严格版本检测 $DoVersionCheck = $true $LatestVersion = "" $InstalledVersion = Get-InstalledVersion if (# 版本已最新 $DoVersionCheck -and $LatestVersion -ne "" -and $InstalledVersion -eq $LatestVersion ) { Write-Output "$AppName已处于最新版本(版本号:$InstalledVersion)。" $DetectionStatus = "UpToDate" $ExitCode = 0 } else {# 下载并更新 Write-Output "正在下载最新MSI安装程序..." Invoke-WebRequest -Uri $InstallerUrl -OutFile $InstallerPath if (# 下载文件无效 !(Test-Path $InstallerPath) -or ((Get-Item $InstallerPath).Length -lt 10000) ) { Write-Output "错误:下载的文件不是有效的安装程序,请检查安装程序URL。" $DetectionStatus = "UpdateFailed" $ExitCode = 2 } else {# 执行更新 Write-Output "通过msiexec静默安装更新..." $InstallProcess = Start-Process ` -FilePath "msiexec.exe" ` -ArgumentList "/i `"$InstallerPath`" /quiet /norestart" ` -Wait ` -PassThru Remove-Item $InstallerPath -Force if ($installProcess.ExitCode -eq 0) {# 更新成功 Write-Output "$AppName已更新,将通过通知告知用户。" Invoke-UserToastNotification $DetectionStatus = "Updated" $ExitCode = 1 } else {# 更新失败 Write-Output "错误:$AppName更新失败,退出代码:$($installProcess.ExitCode)" $DetectionStatus = "UpdateFailed" $ExitCode = 2 } } } Write-Output "DetectionStatus=$DetectionStatus" exit $ExitCode
解决方案:实现无中断的Remote Desktop Client更新
问题根源
你的脚本使用msiexec /i执行更新时,Windows Installer会强制关闭正在运行的Remote Desktop进程——这是MSI的默认行为,因为它需要替换被占用的程序文件。
实现无中断更新的两种方案
方案1:修改MSI参数延迟文件替换
添加REINSTALLMODE=amus参数,让安装程序仅更新注册表、配置项和快捷方式,跳过正在使用的文件替换,待用户下次启动Remote Desktop时自动完成文件更新:
# 替换原脚本中的msiexec启动代码 $InstallProcess = Start-Process ` -FilePath "msiexec.exe" ` -ArgumentList "/i `"$InstallerPath`" /quiet /norestart REINSTALLMODE=amus" ` -Wait ` -PassThru
方案2:检测活跃会话,延迟更新
在执行更新前检查是否有正在运行的Remote Desktop会话,若存在则跳过本次更新,仅通知用户后续重启应用完成更新:
# 在脚本"下载并更新"区块内、执行msiexec前添加以下代码 function Get-ActiveRDSessions { # 查询正在运行的Remote Desktop客户端进程 return Get-Process -Name mstsc -ErrorAction SilentlyContinue } $activeSessions = Get-ActiveRDSessions if ($activeSessions) { Write-Output "检测到正在运行的Remote Desktop会话,将跳过本次更新" Invoke-UserToastNotification -Title "Remote Desktop更新待处理" -Body "检测到您正在使用Remote Desktop,更新将在您下次重启应用时自动完成。" $DetectionStatus = "PendingUpdate" $ExitCode = 3 Write-Output "DetectionStatus=$DetectionStatus" exit $ExitCode }
额外优化建议
- 替换
Win32_Product:Win32_Product会触发MSI自检,可能导致性能问题,建议改用注册表读取版本:
function Get-InstalledVersion { $UninstallKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" $Product = Get-ItemProperty -Path $UninstallKey | Where-Object { $_.DisplayName -eq $MSIProductName } if ($Product) { return $_.DisplayVersion } else { return $null } }
内容的提问来源于stack exchange,提问作者Cg TV
相关产品推荐
相关产品推荐

