非交互模式执行PowerShell脚本时出现Unsupported Pattern异常
问题:PowerShell脚本逐行执行正常,批量运行时UIAutomation报错"Unsupported Pattern"
我想用PowerShell静默卸载程序,流程是通过Winget启动卸载,再用UIAutomation点击卸载确认按钮。逐行交互执行脚本时多次测试都正常,但从文件运行或粘贴整个脚本块到PowerShell时,出现以下错误:
Exception calling "GetCurrentPattern" with "1" argument(s): "Unsupported Pattern."
试过在每个操作后加Start-Sleep排查,没用;也换过不同PowerShell版本、系统上下文运行脚本,问题依旧。完整脚本如下:
#import required assemblies Add-Type -AssemblyName UIAutomationClient Add-Type -AssemblyName UIAutomationTypes #find and run winget (script runs in system context and cant find winget, but directly referencing .exe works) $winget = "$env:programfiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe" &$winget uninstall dropbox --Disable-Interactivity #find drobbox uninstaller process $DBUninstallProc = (Get-Process | Where-Object {$_.ProcessName -eq 'Au_'}) #wait for GUI to load $null = $DBUninstallProc.WaitForInputIdle(5000) #get root element $root = [Windows.Automation.AutomationElement]::RootElement #Create a PropertyCondition used to grab the Uninstaller Window $GetDBWindowCondition = New-Object Windows.Automation.PropertyCondition([Windows.Automation.AutomationElement]::ProcessIdProperty, $DBUninstallProc.Id) #Get the Uninstaller window $DBUninstallerUI = $root.FindFirst([Windows.Automation.TreeScope]::Children, $GetDBWindowCondition) #Find the uninstall button $UninstallButtonCondition = New-Object Windows.Automation.PropertyCondition([Windows.Automation.AutomationElement]::NameProperty, "Uninstall") $button = $DBUninstallerUI.FindFirst([Windows.Automation.TreeScope]::Descendants, $UninstallButtonCondition) #Click the uninstall button $button.GetCurrentPattern([Windows.Automation.InvokePattern]::Pattern) #.Invoke();
解决方案
问题根源是批量运行时,UI元素还未完全初始化就被脚本尝试访问,WaitForInputIdle无法保证UI控件的Pattern已经就绪。可以从以下几点优化:
循环等待窗口和控件就绪
不要只等待进程空闲,要循环检查窗口和按钮元素是否存在,以及控件是否支持InvokePattern:# 等待卸载器窗口出现 $DBUninstallerUI = $null while (-not $DBUninstallerUI) { Start-Sleep -Milliseconds 500 $DBUninstallerUI = $root.FindFirst([Windows.Automation.TreeScope]::Children, $GetDBWindowCondition) } # 等待Uninstall按钮出现并确认支持InvokePattern $button = $null $invokePattern = $null while (-not $invokePattern) { Start-Sleep -Milliseconds 500 $button = $DBUninstallerUI.FindFirst([Windows.Automation.TreeScope]::Descendants, $UninstallButtonCondition) if ($button) { try { $invokePattern = $button.GetCurrentPattern([Windows.Automation.InvokePattern]::Pattern) } catch { # 控件还未就绪,继续等待 $button = $null } } } # 执行点击 $invokePattern.Invoke()改进进程查找逻辑
原脚本中Get-Process | Where-Object {$_.ProcessName -eq 'Au_'}可能匹配多个进程,或者进程还未启动就查找,改成循环查找直到进程存在:$DBUninstallProc = $null while (-not $DBUninstallProc) { Start-Sleep -Milliseconds 500 $DBUninstallProc = Get-Process -Name 'Au_' -ErrorAction SilentlyContinue }调整Winget执行方式
使用Start-Process并等待Winget启动卸载器后再继续,避免脚本超前执行:Start-Process -FilePath $winget -ArgumentList "uninstall dropbox --Disable-Interactivity" -Wait -NoNewWindow
整合后的完整脚本:
# 导入所需程序集 Add-Type -AssemblyName UIAutomationClient Add-Type -AssemblyName UIAutomationTypes # 定位Winget路径(避免通配符匹配多个版本) $wingetPath = Get-ChildItem -Path "$env:programfiles\WindowsApps\Microsoft.DesktopAppInstaller_*_x64__8wekyb3d8bbwe\winget.exe" | Select-Object -Last 1 -ExpandProperty FullName if (-not $wingetPath) { Write-Error "未找到Winget可执行文件" exit 1 } # 启动Winget卸载Dropbox并等待操作完成 Start-Process -FilePath $wingetPath -ArgumentList "uninstall dropbox --Disable-Interactivity" -Wait -NoNewWindow # 循环查找Dropbox卸载器进程 $DBUninstallProc = $null while (-not $DBUninstallProc) { Start-Sleep -Milliseconds 500 $DBUninstallProc = Get-Process -Name 'Au_' -ErrorAction SilentlyContinue } # 等待进程进入空闲状态 $null = $DBUninstallProc.WaitForInputIdle(5000) # 获取UI自动化根元素 $root = [Windows.Automation.AutomationElement]::RootElement # 创建卸载窗口查找条件(按进程ID匹配) $windowCondition = New-Object Windows.Automation.PropertyCondition([Windows.Automation.AutomationElement]::ProcessIdProperty, $DBUninstallProc.Id) # 循环等待卸载窗口出现 $uninstallWindow = $null while (-not $uninstallWindow) { Start-Sleep -Milliseconds 500 $uninstallWindow = $root.FindFirst([Windows.Automation.TreeScope]::Children, $windowCondition) } # 创建卸载按钮查找条件(按名称匹配) $buttonCondition = New-Object Windows.Automation.PropertyCondition([Windows.Automation.AutomationElement]::NameProperty, "Uninstall") # 循环等待按钮就绪并获取InvokePattern $invokePattern = $null while (-not $invokePattern) { Start-Sleep -Milliseconds 500 $button = $uninstallWindow.FindFirst([Windows.Automation.TreeScope]::Descendants, $buttonCondition) if ($button) { try { $invokePattern = $button.GetCurrentPattern([Windows.Automation.InvokePattern]::Pattern) } catch { # 控件未完全初始化,重置按钮对象继续等待 $button = $null } } } # 执行按钮点击操作 $invokePattern.Invoke()
内容的提问来源于stack exchange,提问作者jonathan
相关产品推荐
相关产品推荐

