Skip to content

fix: Windows Features says why a change failed, reads each feature back afterwards, and no longer stops a change half-way - #2940

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/windows-features-reads-back-and-says-why
Oct 21, 2026
Merged

laurentiu021 merged 1 commit into
mainfrom
fix/windows-features-reads-back-and-says-why

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

Closes #2739. Parts 2, 3 and 4 of #2834 for Windows Features.

Confirmed in the code: both change scripts ended in catch { exit 1 }, so the view model received a bare false and said "Failed to … Check permissions." for every failure, on a tab that offers a change only to an administrator. After a change it flipped the row's IsEnabled on success and left it on failure, and ParseFeatureList counted only Enabled as on. EscapeCancel and the Cancel button both followed IsBusy, which covers a change, and Cancel cancelled the change's token, which RunProcessAsync answers by ending powershell.exe and its DismHost.exe. The switch is bound one-way, and nothing told it to read IsEnabled again when the change did not happen (#2739).

What I checked on my PC, without changing anything:

  • Enabling a feature name Windows does not know throws a COMException with HResult 0x800F080C, "Feature name … is unknown.", and without administrator rights 0x800702E4, "The requested operation requires elevation.". So the cmdlet's code is in $_.Exception.HResult.
  • Get-WindowsOptionalFeature -Online for every feature took 54 seconds here, and for one feature about a second, so the tab reads back only the feature it changed.
  • The cmdlet's states are Disabled, DisablePending, Enabled, EnablePending, Superseded, PartiallyInstalled and DisabledWithPayloadRemoved.
  • Windows names MSRDC-Infrastructure "Remote Differential Compression API Support" ("Installs Remote Differential Compression (RDC) support for use in third-party applications."), Containers-DisposableClientVM "Windows Sandbox", WindowsMediaPlayer "Windows Media Player Legacy (App)" and MediaPlayback "Media Features" ("Controls media features such as Windows Media Player."). Microsoft-Windows-Client-EmbeddedExp-Package is not on this PC.

The changes:

  • WindowsFeaturesService.ChangeScript: the catch prints FailureMarker, the code in hex and the first line of DISM's message before exit 1, and ParseChange returns them in a new FeatureChangeResult. The service logs the refusal.
  • WindowsFeaturesViewModel.DescribeFailure: "Failed to enable X (0x800F081F)." followed by plain words for four codes, three from Microsoft's article on .NET Framework 3.5 installation errors (KB 2734782): files Windows could not download (0x800F0906) or find (0x800F081F) on Windows Update, a policy that forbids downloading them (0x800F0907); and a service it needs that is turned off (0x80070422). Any other code is followed by DISM's own reason, in Windows' language. Nothing says "Check permissions" any more.
  • ReadFeatureAsync reads one feature through the query the list uses, and after every change the row is written from it: a failure that did part of its work shows what it did, and a success that Windows reports as not having moved says so and suggests a scan. Without a read-back the row goes by DISM's answer, as before. ReadState counts EnablePending as on and both pending states as "Reboot required", in a scan too, and a pending row turns on the reboot notice. A feature waiting for a restart to turn on read as off.
  • WindowsFeature.ResetSwitch, called from a finally around the whole command, raises the change notification for IsEnabled, so the switch turns back after a decline, a busy lock, a missing elevation or a failure.
  • EscapeCancel returns CancelCommand only while the new IsScanning is set, the Cancel button shows only then, and Cancel cancels only the scan.
  • SafetyDatabase: the hints for MSRDC-Infrastructure, WindowsMediaPlayer and MediaPlayback describe those features in Windows' names, and Windows Sandbox's rating is under Containers-DisposableClientVM.

Still open on #2834: part 1 (a change that turns on parent features through -All should name them first and offer them when the feature is turned off), part 3 for Uninstaller and System Fixes, and part 5 (where the admin banner sits).

Docs

README, Windows Features: the read-back, the failure reason and Cancel stopping only a scan. ARCHITECTURE: WindowsFeaturesViewModel and WindowsFeaturesService.

Tests

  • New in WindowsFeaturesTests: a refusal with 0x800F081F says the plain sentence and its code, another code says DISM's reason, each of the four codes gets its own sentence, a failure with no code says Windows did not say why; a change waiting for a restart shows as on with "Reboot required", a failure that changed the feature shows the change, a failed change that left a restart pending turns on the notice, a success Windows reports as not having moved says so, and without a read-back the row goes by DISM; the switch is told to read the state again after a decline, a busy lock, a missing elevation and a refusal; Escape and Cancel stop a scan and not a change; a scan shows a pending feature as on; the five states; ParseChange; -All on enable only; and Enable, Disable and the read-back refuse a name that is not a feature name, which nothing tested.
  • New in SafetyDatabaseTests: the four hints begin with Windows' name for the feature, and Windows Sandbox is rated under its own name only.
  • New WindowsFeatureScriptTests (integration) run the change script and the query in Windows PowerShell with the cmdlets shadowed: a refusal prints the marker, 800F081F and the first line and exits 1, and ParseChange reads that output; a change that goes through prints the restart flag; and the cmdlet's own EnablePending reads as on.
  • EveryCancellableTab_LetsEscapeReachItsOwnCancelCommand holds the Cancel button and EscapeCancel to the same flag.
  • Red before the fix, green after, each undone on its own: the switch not told, the silent catch, "Check permissions", only Enabled counted as on, the read-back ignored, Escape following IsBusy, Cancel stopping the change, the button following IsBusy (the guard), the scan ignoring a pending feature, the reboot notice only on success, the old hints, the read-back not checking its name, and -All on disable.
  • Run locally, twice: WindowsFeaturesTests, SafetyDatabaseTests, ArchitectureTests, AcceleratorRoutingTests, SystemPathsTests and InclusiveTermsTests, and the integration WindowsFeatureScriptTests. All four projects build with 0 warnings, and dotnet format --verify-no-changes is clean. The tab has not been looked at on screen.

…ck afterwards, and no longer stops a change half-way

- WindowsFeaturesService's change scripts print DISM's error code and the first line of its message before they exit 1,
  and ParseChange returns both in a FeatureChangeResult. The tab says that reason, in plain words for 0x800F0906,
  0x800F081F, 0x800F0907 and 0x80070422, where every failure said "Check permissions".
- After every change the view model reads the feature back with ReadFeatureAsync and writes the row from what Windows
  reports. ReadState counts EnablePending as on and both pending states as waiting for a restart, in a scan too.
- WindowsFeature.ResetSwitch turns the switch back whenever a change was declined, refused or failed.
- Escape and the Cancel button stop only a scan, while IsScanning is set. They ended DISM part-way through a change.
- SafetyDatabase describes MSRDC-Infrastructure, WindowsMediaPlayer and MediaPlayback as the features they are, and
  rates Windows Sandbox under Containers-DisposableClientVM, its own name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant