You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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
}

额外优化建议

  1. 替换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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 23:17:05