Fix UI helper errors during headless -Preset and -Config runs - #4941
Conversation
The -Preset and -Config paths run Invoke-WinUtilAutoRun before the XAML form is created and before PresentationCore is loaded, so every call into Invoke-WPFUIThread failed with InvokeMethodOnNull and every call into Set-WinUtilTweaksProgressIndicator failed to resolve [Windows.Visibility]. The errors were non-terminating, so tweaks still applied, but each run filled the log with noise. Loading presentationframework earlier does not help: Visibility lives in PresentationCore, which only loads once a WPF object is instantiated, and the XAML-named progress controls do not exist in these paths either. Guarding the two helpers covers Invoke-WPFtweaksbutton, Invoke-WPFundoall and Invoke-WPFFeatureInstall, which the existing per-call-site $hasUI convention never reached.
Invoke-WPFRunspace returns an IAsyncResult that no caller uses, and Set-WinUtilRegistry was the only New-PSDrive call site that did not suppress its output. A GUI click handler discards both, but the -Preset and -Config paths call the workflows directly, so an async handle dump and a PSDrive table landed in the user's log. The handle itself is kept because pester/runspace.Tests.ps1 asserts that Invoke-WPFRunspace returns a single IAsyncResult, so the suppression goes at the four call sites reachable from Invoke-WinUtilAutoRun.
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughSummary by CodeRabbit
WalkthroughWPF helpers now no-op when the form or dispatcher is unavailable. Headless Pester tests cover these paths and initialized controls. Registry and runspace commands now suppress incidental pipeline output. Project guidance documents the UI-safety requirement. ChangesUI safety and output control
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Type of Change
Description
Running WinUtil headlessly with
-Presetor-Configwrites a wall of errors into the session log on every run.scripts/main.ps1handles-Preset(lines 37-51) and-Config(lines 53-65) in early-return blocks that callInvoke-WinUtilAutoRunand then exit. Both return before line 67 loadspresentationframeworkand before line 74 creates$sync["Form"], so the tweak workflow calls UI-only helpers against a window that does not exist:Invoke-WPFUIThreadruns$sync.form.Dispatcher.Invoke(...)on$nulland throwsInvokeMethodOnNullSet-WinUtilTweaksProgressIndicatorcannot resolve[Windows.Visibility]and throwsTypeNotFoundThe errors are non-terminating, so tweaks still apply correctly, but a
-Preset Advancedrun logs six error records before the first tweak even starts. Sample from a real run:Why the guard goes in the helpers
Loading the WPF assembly earlier does not fix this.
System.Windows.Visibilitylives in PresentationCore, which only loads once a WPF object is actually instantiated, soLoadWithPartialName('presentationframework')leaves the type error unchanged. And giving$sync.Forma real window only trades the dispatcher errors forPropertyNotFoundonWPFTweaksProgressBar,WPFTweaksProgressLabelandWPFTweaksProgressValue, which are only populated atmain.ps1:135.The repo already has a
$hasUI = $null -ne $sync.Form -and $null -ne $sync.Form.Dispatcherconvention inInvoke-WPFInstall,Invoke-WPFUnInstall,Invoke-WPFAppxInstall,Invoke-WPFAppxRemovalandInvoke-WPFOOSU— but it was never applied toInvoke-WPFtweaksbutton,Invoke-WPFundoallorInvoke-WPFFeatureInstall, which is exactly why these errors appear. Guarding the two helpers covers all three missed workflows in two lines instead of ~15, and any future call site along with them. The existing$hasUIblocks become redundant rather than wrong and are left untouched.Stray console output
A second commit removes two pieces of output that a GUI click handler silently discards but that reach the console on automation runs:
Invoke-WPFRunspacereturns anIAsyncResultthat no call site uses, printing aCompletedSynchronously / IsCompletedblock. The return value itself is kept becausepester/runspace.Tests.ps1asserts it, so the suppression is applied at the four call sites reachable fromInvoke-WinUtilAutoRun.Set-WinUtilRegistrywas the onlyNew-PSDrivecall site not piping toOut-Null, printing a PSDrive table mid-run.Invoke-WinUtilCurrentSystemandGet-WinUtilToggleStatusalready suppress it.Testing
pester/headless-ui.Tests.ps1(5 tests) covers the no-window cases for both helpers and asserts the GUI path still marshals onto the dispatcher and still updates the indicator controls. The three no-window tests were confirmed to fail against the pre-fix functions, and the two GUI-path tests pass both before and after, so the guard provably does not disable the GUI behaviour..\Compile.ps1succeeds.lint/PSScriptAnalyser.ps1on the changed files: no new findings.-Preset Advancedrun that all 17 tweaks apply both before and after this change, including all 12 registry keys forWPFTweaksTelemetry. This PR only removes the error noise; it does not change which tweaks run.No behaviour change in the GUI: when a window exists, both helpers work exactly as before.
Issue related to PR