From 07ccd8e2e755a706f31569808b31f5b77acad6a9 Mon Sep 17 00:00:00 2001 From: Omar <90123670+mewclouds@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:43:22 -0400 Subject: [PATCH] fix(tweaks): skip missing services instead of aborting the run (#5140) Invoke-WinUtilTweaks caught a missing service with [System.ServiceProcess.ServiceNotFoundException], a type that does not exist in .NET. PowerShell could not resolve the catch type, so the missing-service case aborted the whole tweaks job rather than warning. Catch on the error ID the way Set-WinUtilService already does: an absent service warns and logs, and any unrelated lookup failure is rethrown. The Tweaks guide documents the skip. --- docs/src/content/docs/guides/tweaks.mdx | 2 + functions/private/Invoke-WinUtilTweaks.ps1 | 13 +++-- pester/tweaks.Tests.ps1 | 61 ++++++++++++++++++++++ 3 files changed, 73 insertions(+), 3 deletions(-) diff --git a/docs/src/content/docs/guides/tweaks.mdx b/docs/src/content/docs/guides/tweaks.mdx index 5f03f0b8..20209f0b 100644 --- a/docs/src/content/docs/guides/tweaks.mdx +++ b/docs/src/content/docs/guides/tweaks.mdx @@ -29,6 +29,8 @@ Use the quick-selection buttons at the top of the Tweaks tab to speed up setup: To see what each preset includes, view [preset.json](https://github.com/ChrisTitusTech/winutil/blob/main/config/preset.json). ::: +When a listed service does not exist, WinUtil logs a warning and skips it. The remaining services and tweaks still run. + :::caution Some tweaks take effect immediately, while others may require Explorer to restart, a sign-out, or a full reboot. ::: diff --git a/functions/private/Invoke-WinUtilTweaks.ps1 b/functions/private/Invoke-WinUtilTweaks.ps1 index f0d1ea8d..284ba8d3 100644 --- a/functions/private/Invoke-WinUtilTweaks.ps1 +++ b/functions/private/Invoke-WinUtilTweaks.ps1 @@ -48,14 +48,21 @@ function Invoke-WinUtilTweaks { # The check for !($undo) is required, without it the script will throw an error for accessing unavailable member, which's the 'OriginalService' Property if ($KeepServiceStartup -AND !($undo)) { + $serviceName = $psitem.Name try { # Check if the service exists - $service = Get-Service -Name $psitem.Name -ErrorAction Stop + $service = Get-Service -Name $serviceName -ErrorAction Stop if(!($service.StartType.ToString() -eq $psitem.$($values.OriginalService))) { $changeservice = $false } - } catch [System.ServiceProcess.ServiceNotFoundException] { - Write-Warning "Service $($psitem.Name) was not found." + } catch { + if ($_.FullyQualifiedErrorId -like "NoServiceFoundForGivenName,*") { + $changeservice = $false + Write-Warning "Service $serviceName was not found." + Write-WinUtilLog -Level "WARN" -Component "Service" -Message "Service $serviceName was not found." + } else { + throw + } } } diff --git a/pester/tweaks.Tests.ps1 b/pester/tweaks.Tests.ps1 index 31805303..46241be6 100644 --- a/pester/tweaks.Tests.ps1 +++ b/pester/tweaks.Tests.ps1 @@ -177,6 +177,67 @@ Describe "Invoke-WinUtilTweaks" { $Name -eq "DiagTrack" -and $StartupType -eq "Disabled" } } + + It "skips a missing service and applies the remaining services and tweaks" { + $script:sync.configs.tweaks.WPFTweaksExample.service = @( + [pscustomobject]@{ + Name = "CscService" + StartupType = "Disabled" + OriginalType = "Manual" + } + ) + $script:sync.configs.tweaks.WPFTweaksExample.service + + Mock Get-Service { + $exception = [Microsoft.PowerShell.Commands.ServiceCommandException]::new("Cannot find any service with service name '$Name'.") + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + $exception, + "NoServiceFoundForGivenName,Microsoft.PowerShell.Commands.GetServiceCommand", + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $Name + ) + throw $errorRecord + } -ParameterFilter { $Name -eq "CscService" -and $ErrorAction -eq "Stop" } + + foreach ($tweak in @("WPFTweaksExample", "WPFTweaksServiceOnly")) { + Invoke-WinUtilTweaks -CheckBox $tweak + } + + Should -Invoke -CommandName Write-Warning -Times 1 -Exactly -ParameterFilter { + $Message -eq "Service CscService was not found." + } + Should -Invoke -CommandName Write-WinUtilLog -Times 1 -Exactly -ParameterFilter { + $Level -eq "WARN" -and $Component -eq "Service" -and $Message -eq "Service CscService was not found." + } + Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly -ParameterFilter { + $Name -eq "CscService" + } + Should -Invoke -CommandName Set-WinUtilService -Times 2 -Exactly -ParameterFilter { + $Name -eq "DiagTrack" -and $StartupType -eq "Disabled" + } + Should -Invoke -CommandName Invoke-WinUtilScript -Times 1 -Exactly -ParameterFilter { + $Name -eq "WPFTweaksExample" + } + } + + It "rethrows service lookup errors that do not identify a missing service" { + Mock Get-Service { + $exception = [Microsoft.PowerShell.Commands.ServiceCommandException]::new("Service lookup failed.") + $errorRecord = [System.Management.Automation.ErrorRecord]::new( + $exception, + "ServiceLookupFailed", + [System.Management.Automation.ErrorCategory]::OpenError, + $Name + ) + throw $errorRecord + } + + { Invoke-WinUtilTweaks -CheckBox "WPFTweaksExample" } | Should -Throw -ExpectedMessage "*Service lookup failed.*" + + Should -Invoke -CommandName Set-WinUtilService -Times 0 -Exactly + Should -Invoke -CommandName Set-WinUtilRegistry -Times 0 -Exactly + Should -Invoke -CommandName Invoke-WinUtilScript -Times 0 -Exactly + Should -Invoke -CommandName Write-Warning -Times 0 -Exactly + } } Describe "Invoke-WinUtilTweaks completion status" {