mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-08-23 08:02:07 +00:00
Refactor feature management scripts to improve error handling
This commit is contained in:
@@ -12,19 +12,15 @@ function Import-RegistryFile {
|
||||
|
||||
if (-not (Test-Path $regFilePath)) {
|
||||
$errorMessage = "Unable to find registry file: $path ($regFilePath)"
|
||||
$script:RegistryImportFailures++
|
||||
Write-Host "Error: $errorMessage" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
throw $errorMessage
|
||||
return $false
|
||||
}
|
||||
|
||||
$importScript = {
|
||||
param($targetRegFilePath, $hiveContext)
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath
|
||||
Write-Host ""
|
||||
return
|
||||
return (Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath)
|
||||
}
|
||||
|
||||
# When the target user's hive is already loaded under their SID, the .reg file's
|
||||
@@ -33,10 +29,11 @@ function Import-RegistryFile {
|
||||
$usePowerShellFallbackOnly = $hiveContext -and [bool]$hiveContext.WasAlreadyLoaded
|
||||
|
||||
if ($usePowerShellFallbackOnly) {
|
||||
Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath
|
||||
Write-Host "The operation completed successfully via PowerShell registry writer."
|
||||
Write-Host ""
|
||||
return
|
||||
$fallbackSucceeded = Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath
|
||||
if ($fallbackSucceeded) {
|
||||
Write-Host "The operation completed successfully via PowerShell registry writer."
|
||||
}
|
||||
return $fallbackSucceeded
|
||||
}
|
||||
|
||||
$regResult = Invoke-NonBlocking -ScriptBlock {
|
||||
@@ -89,26 +86,29 @@ function Import-RegistryFile {
|
||||
if (-not $hasSuccess) {
|
||||
$details = if ($regResult.Error) { $regResult.Error } else { "Exit code: $($regResult.ExitCode)" }
|
||||
Write-Warning "reg import failed for '$path'. Falling back to PowerShell registry writer. Details: $details"
|
||||
Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath
|
||||
Write-Host "The operation completed successfully via PowerShell registry writer."
|
||||
$fallbackSucceeded = Invoke-RegistryOperationsFromRegFile -RegFilePath $targetRegFilePath
|
||||
if ($fallbackSucceeded) {
|
||||
Write-Host "The operation completed successfully via PowerShell registry writer."
|
||||
}
|
||||
return $fallbackSucceeded
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
return $true
|
||||
}
|
||||
|
||||
try {
|
||||
if ($usesOfflineHive) {
|
||||
# Sysprep targets Default user, User targets the specified user. Logged-in users already have their hive mounted under HKU\<SID>.
|
||||
$targetUserName = if ($script:Params.ContainsKey("Sysprep")) { "Default" } else { $script:Params.Item("User") }
|
||||
Invoke-WithTargetUserHive -TargetUserName $targetUserName -ScriptBlock $importScript -ArgumentObject $regFilePath -PassHiveContext
|
||||
$succeeded = Invoke-WithTargetUserHive -TargetUserName $targetUserName -ScriptBlock $importScript -ArgumentObject $regFilePath -PassHiveContext
|
||||
}
|
||||
else {
|
||||
& $importScript $regFilePath $null
|
||||
$succeeded = & $importScript $regFilePath $null
|
||||
}
|
||||
return [bool]$succeeded
|
||||
}
|
||||
catch {
|
||||
$script:RegistryImportFailures++
|
||||
Write-Host $_.Exception.Message -ForegroundColor Red
|
||||
Write-Host ""
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
- Registry-backed: imports the .reg file via Import-RegistryFile, then runs
|
||||
any post-import side effects (e.g., removing companion app packages).
|
||||
- Custom logic: app removal, Windows optional features, start menu
|
||||
replacement, and other special-case features.
|
||||
replacement, and other special-case features. Returns $true when the
|
||||
feature completes successfully; otherwise writes a warning and returns
|
||||
$false.
|
||||
#>
|
||||
function Invoke-FeatureApply {
|
||||
param(
|
||||
@@ -15,30 +17,32 @@ function Invoke-FeatureApply {
|
||||
[string]$FeatureId
|
||||
)
|
||||
|
||||
# Resolve feature metadata from Features.json
|
||||
$feature = $script:Features[$FeatureId]
|
||||
$applyText = $feature.ApplyText
|
||||
try {
|
||||
# Resolve feature metadata from Features.json
|
||||
$feature = $script:Features[$FeatureId]
|
||||
$applyText = $feature.ApplyText
|
||||
|
||||
# ---- Registry-backed features: import .reg file, then handle side effects ----
|
||||
# ---- Registry-backed features: import .reg file, then handle additional tasks ----
|
||||
if ($feature.RegistryKey) {
|
||||
Import-RegistryFile "> $applyText..." $feature.RegistryKey
|
||||
if (-not (Import-RegistryFile "> $applyText..." $feature.RegistryKey)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
# Post-import side effects for specific features
|
||||
switch ($FeatureId) {
|
||||
'DisableBing' {
|
||||
# Also remove the app package for Bing search
|
||||
Remove-SelectedApps @('Microsoft.BingSearch')
|
||||
return (Remove-SelectedApps @('Microsoft.BingSearch'))
|
||||
}
|
||||
'DisableCopilot' {
|
||||
# Also remove the app packages for Copilot
|
||||
Remove-SelectedApps @('Microsoft.Copilot', 'XP9CXNGPPJ97XX')
|
||||
return (Remove-SelectedApps @('Microsoft.Copilot', 'XP9CXNGPPJ97XX'))
|
||||
}
|
||||
'DisableTelemetry' {
|
||||
# Also disable telemetry scheduled tasks
|
||||
Disable-TelemetryScheduledTasks
|
||||
return (Disable-TelemetryScheduledTasks)
|
||||
}
|
||||
}
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
# ---- Custom features (no registry backing, or special handling required) ----
|
||||
@@ -49,30 +53,25 @@ function Invoke-FeatureApply {
|
||||
|
||||
if ($appsList.Count -eq 0) {
|
||||
Write-Host "No valid apps were selected for removal" -ForegroundColor Yellow
|
||||
Write-Host ""
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Host "$($appsList.Count) apps selected for removal"
|
||||
Remove-SelectedApps $appsList
|
||||
return
|
||||
return (Remove-SelectedApps $appsList)
|
||||
}
|
||||
'RemoveGamingApps' {
|
||||
$appsList = @('Microsoft.GamingApp', 'Microsoft.XboxGameOverlay', 'Microsoft.XboxGamingOverlay')
|
||||
Write-Host "> $applyText..."
|
||||
Remove-SelectedApps $appsList
|
||||
return
|
||||
return (Remove-SelectedApps $appsList)
|
||||
}
|
||||
'RemoveHPApps' {
|
||||
$appsList = @('AD2F1837.HPAIExperienceCenter', 'AD2F1837.HPJumpStarts', 'AD2F1837.HPPCHardwareDiagnosticsWindows', 'AD2F1837.HPPowerManager', 'AD2F1837.HPPrivacySettings', 'AD2F1837.HPSupportAssistant', 'AD2F1837.HPSureShieldAI', 'AD2F1837.HPSystemInformation', 'AD2F1837.HPQuickDrop', 'AD2F1837.HPWorkWell', 'AD2F1837.myHP', 'AD2F1837.HPDesktopSupportUtilities', 'AD2F1837.HPQuickTouch', 'AD2F1837.HPEasyClean', 'AD2F1837.HPConnectedMusic', 'AD2F1837.HPFileViewer', 'AD2F1837.HPRegistration', 'AD2F1837.HPWelcome', 'AD2F1837.HPConnectedPhotopoweredbySnapfish', 'AD2F1837.HPPrinterControl')
|
||||
Write-Host "> $applyText..."
|
||||
Remove-SelectedApps $appsList
|
||||
return
|
||||
return (Remove-SelectedApps $appsList)
|
||||
}
|
||||
'ForceRemoveEdge' {
|
||||
Write-Host "> $applyText..."
|
||||
Invoke-ForceRemoveEdge
|
||||
return
|
||||
return (Invoke-ForceRemoveEdge)
|
||||
}
|
||||
'DisableWidgets' {
|
||||
Write-Host "> $applyText..."
|
||||
@@ -81,76 +80,75 @@ function Invoke-FeatureApply {
|
||||
Get-Process *Widget* -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Remove-SelectedApps @('Microsoft.StartExperiencesApp','MicrosoftWindows.Client.WebExperience','Microsoft.WidgetsPlatformRuntime')
|
||||
return
|
||||
return (Remove-SelectedApps @('Microsoft.StartExperiencesApp','MicrosoftWindows.Client.WebExperience','Microsoft.WidgetsPlatformRuntime'))
|
||||
}
|
||||
'EnableWindowsSandbox' {
|
||||
Write-Host "> $applyText..."
|
||||
Enable-WindowsFeature "Containers-DisposableClientVM"
|
||||
Write-Host ""
|
||||
return
|
||||
return (Enable-WindowsFeature "Containers-DisposableClientVM")
|
||||
}
|
||||
'EnableWindowsSubsystemForLinux' {
|
||||
Write-Host "> $applyText..."
|
||||
Enable-WindowsFeature "VirtualMachinePlatform"
|
||||
Enable-WindowsFeature "Microsoft-Windows-Subsystem-Linux"
|
||||
Write-Host ""
|
||||
return
|
||||
if (-not (Enable-WindowsFeature "VirtualMachinePlatform")) { return $false }
|
||||
return (Enable-WindowsFeature "Microsoft-Windows-Subsystem-Linux")
|
||||
}
|
||||
'ClearStart' {
|
||||
Write-Host "> $applyText for user $(Get-UserName)..."
|
||||
$startMenuBinFile = Get-StartMenuBinPathForUser -UserName (Get-UserName)
|
||||
if (-not [string]::IsNullOrWhiteSpace($startMenuBinFile)) {
|
||||
Replace-StartMenu -startMenuBinFile $startMenuBinFile
|
||||
return (Replace-StartMenu -startMenuBinFile $startMenuBinFile)
|
||||
}
|
||||
Write-Host ""
|
||||
return
|
||||
Write-Warning "Unable to apply '$applyText': the Start menu path for user $(Get-UserName) could not be resolved."
|
||||
return $false
|
||||
}
|
||||
'ReplaceStart' {
|
||||
Write-Host "> $applyText for user $(Get-UserName)..."
|
||||
$startMenuBinFile = Get-StartMenuBinPathForUser -UserName (Get-UserName)
|
||||
if (-not [string]::IsNullOrWhiteSpace($startMenuBinFile)) {
|
||||
Replace-StartMenu -startMenuBinFile $startMenuBinFile -startMenuTemplate $script:Params.Item("ReplaceStart")
|
||||
return (Replace-StartMenu -startMenuBinFile $startMenuBinFile -startMenuTemplate $script:Params.Item("ReplaceStart"))
|
||||
}
|
||||
Write-Host ""
|
||||
return
|
||||
Write-Warning "Unable to apply '$applyText': the Start menu path for user $(Get-UserName) could not be resolved."
|
||||
return $false
|
||||
}
|
||||
'ClearStartAllUsers' {
|
||||
Replace-StartMenuForAllUsers
|
||||
return
|
||||
return (Replace-StartMenuForAllUsers)
|
||||
}
|
||||
'ReplaceStartAllUsers' {
|
||||
Replace-StartMenuForAllUsers -startMenuTemplate $script:Params.Item("ReplaceStartAllUsers")
|
||||
return
|
||||
return (Replace-StartMenuForAllUsers -startMenuTemplate $script:Params.Item("ReplaceStartAllUsers"))
|
||||
}
|
||||
'DisableStoreSearchSuggestions' {
|
||||
if ($script:Params.ContainsKey("Sysprep")) {
|
||||
Write-Host "> Disabling Microsoft Store search suggestions in the start menu for all users..."
|
||||
Set-StoreSearchSuggestionsDisabledForAllUsers
|
||||
Write-Host ""
|
||||
return
|
||||
return (Set-StoreSearchSuggestionsDisabledForAllUsers)
|
||||
}
|
||||
|
||||
Write-Host "> Disabling Microsoft Store search suggestions for user $(Get-UserName)..."
|
||||
$storeDb = Get-StoreAppsDatabasePathForUser -UserName (Get-UserName)
|
||||
if ($storeDb) {
|
||||
Set-StoreSearchSuggestionsDisabled -StoreAppsDatabase $storeDb
|
||||
return (Set-StoreSearchSuggestionsDisabled -StoreAppsDatabase $storeDb)
|
||||
}
|
||||
Write-Host ""
|
||||
return
|
||||
Write-Warning "Unable to disable Microsoft Store search suggestions because the Store database for user $(Get-UserName) could not be resolved."
|
||||
return $false
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to apply '$applyText': $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Warning "Unknown feature '$FeatureId' could not be applied."
|
||||
return $false
|
||||
}
|
||||
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Undoes a single feature that has no RegistryUndoKey.
|
||||
Undoes a single feature.
|
||||
|
||||
.DESCRIPTION
|
||||
Handles undo for features that require custom logic rather than a simple
|
||||
.reg file import. Features with a RegistryUndoKey are handled directly
|
||||
via Import-RegistryFile in Invoke-UndoFeatures.
|
||||
Handles registry-backed undo imports and custom undo logic. Returns
|
||||
$true when the requested undo succeeds; otherwise writes a warning and
|
||||
returns $false.
|
||||
#>
|
||||
function Invoke-FeatureUndo {
|
||||
param(
|
||||
@@ -159,43 +157,65 @@ function Invoke-FeatureUndo {
|
||||
)
|
||||
|
||||
$feature = if ($script:Features.ContainsKey($FeatureId)) { $script:Features[$FeatureId] } else { $null }
|
||||
if (-not $feature) {
|
||||
Write-Warning "Unknown feature '$FeatureId' could not be undone."
|
||||
return $false
|
||||
}
|
||||
|
||||
switch ($FeatureId) {
|
||||
'DisableStoreSearchSuggestions' {
|
||||
if ($script:Params.ContainsKey('Sysprep')) {
|
||||
Write-Host "> Re-enabling Microsoft Store search suggestions in the start menu for all users..."
|
||||
Set-StoreSearchSuggestionsEnabledForAllUsers
|
||||
Write-Host ""
|
||||
return
|
||||
$undoText = if ($feature.ApplyUndoText) { $feature.ApplyUndoText } elseif ($feature.UndoLabel) { $feature.UndoLabel } else { $FeatureId }
|
||||
|
||||
try {
|
||||
# ---- Registry-backed features: import undo data, then handle additional tasks ----
|
||||
if ($feature.RegistryUndoKey) {
|
||||
if (-not (Import-RegistryFile "> $undoText" (Resolve-UndoRegFilePath $feature.RegistryUndoKey))) {
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host "> Re-enabling Microsoft Store search suggestions for user $(Get-UserName)..."
|
||||
$storeDb = Get-StoreAppsDatabasePathForUser -UserName (Get-UserName)
|
||||
if ($storeDb) {
|
||||
Set-StoreSearchSuggestionsEnabled -StoreAppsDatabase $storeDb
|
||||
switch ($FeatureId) {
|
||||
'DisableTelemetry' {
|
||||
# Also re-enable telemetry scheduled tasks.
|
||||
return (Enable-TelemetryScheduledTasks)
|
||||
}
|
||||
}
|
||||
Write-Host ""
|
||||
return
|
||||
|
||||
return $true
|
||||
}
|
||||
'EnableWindowsSandbox' {
|
||||
Write-Host "> $($feature.ApplyUndoText)..."
|
||||
Disable-WindowsFeature 'Containers-DisposableClientVM'
|
||||
Write-Host ""
|
||||
return
|
||||
}
|
||||
'EnableWindowsSubsystemForLinux' {
|
||||
Write-Host "> $($feature.ApplyUndoText)..."
|
||||
Disable-WindowsFeature 'Microsoft-Windows-Subsystem-Linux'
|
||||
Disable-WindowsFeature 'VirtualMachinePlatform'
|
||||
Write-Host ""
|
||||
return
|
||||
}
|
||||
'DisableTelemetry' {
|
||||
# Also re-enable telemetry scheduled tasks
|
||||
Enable-TelemetryScheduledTasks
|
||||
return
|
||||
|
||||
# ---- Custom undo features (no registry backing) ----
|
||||
switch ($FeatureId) {
|
||||
'DisableStoreSearchSuggestions' {
|
||||
if ($script:Params.ContainsKey('Sysprep')) {
|
||||
Write-Host "> Re-enabling Microsoft Store search suggestions in the start menu for all users..."
|
||||
return (Set-StoreSearchSuggestionsEnabledForAllUsers)
|
||||
}
|
||||
|
||||
Write-Host "> Re-enabling Microsoft Store search suggestions for user $(Get-UserName)..."
|
||||
$storeDb = Get-StoreAppsDatabasePathForUser -UserName (Get-UserName)
|
||||
if ($storeDb) {
|
||||
return (Set-StoreSearchSuggestionsEnabled -StoreAppsDatabase $storeDb)
|
||||
}
|
||||
Write-Warning "Unable to re-enable Microsoft Store search suggestions because the Store database for user $(Get-UserName) could not be resolved."
|
||||
return $false
|
||||
}
|
||||
'EnableWindowsSandbox' {
|
||||
Write-Host "> $($feature.ApplyUndoText)..."
|
||||
return (Disable-WindowsFeature 'Containers-DisposableClientVM')
|
||||
}
|
||||
'EnableWindowsSubsystemForLinux' {
|
||||
Write-Host "> $($feature.ApplyUndoText)..."
|
||||
if (-not (Disable-WindowsFeature 'Microsoft-Windows-Subsystem-Linux')) { return $false }
|
||||
return (Disable-WindowsFeature 'VirtualMachinePlatform')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch {
|
||||
Write-Warning "Failed to undo '$undoText': $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Warning "Feature '$FeatureId' does not support undo."
|
||||
return $false
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +271,11 @@ function Invoke-ApplyFeatures {
|
||||
& $script:ApplyProgressCallback $step $TotalSteps $displayName
|
||||
}
|
||||
|
||||
Invoke-FeatureApply -FeatureId $featureId
|
||||
if (-not (Invoke-FeatureApply -FeatureId $featureId)) {
|
||||
$script:ApplyFeatureFailures++
|
||||
$script:FeatureFailures++
|
||||
}
|
||||
Write-Host ""
|
||||
$step++
|
||||
}
|
||||
}
|
||||
@@ -262,9 +286,8 @@ function Invoke-ApplyFeatures {
|
||||
Undoes a list of features, reporting progress for each.
|
||||
|
||||
.DESCRIPTION
|
||||
Iterates through the provided feature IDs. Features with a RegistryUndoKey
|
||||
are handled by importing the undo .reg file; all others delegate to
|
||||
Invoke-FeatureUndo for custom undo logic.
|
||||
Iterates through the provided feature IDs and delegates each to
|
||||
Invoke-FeatureUndo, which handles registry-backed and custom undo logic.
|
||||
This is called by Invoke-AllChanges during the undo phase.
|
||||
#>
|
||||
function Invoke-UndoFeatures {
|
||||
@@ -291,11 +314,11 @@ function Invoke-UndoFeatures {
|
||||
& $script:ApplyProgressCallback $step $TotalSteps $undoText
|
||||
}
|
||||
|
||||
if ($f -and $f.RegistryUndoKey) {
|
||||
Import-RegistryFile "> $undoText" (Resolve-UndoRegFilePath $f.RegistryUndoKey)
|
||||
if (-not (Invoke-FeatureUndo -FeatureId $featureId)) {
|
||||
$script:UndoFeatureFailures++
|
||||
$script:FeatureFailures++
|
||||
}
|
||||
|
||||
Invoke-FeatureUndo -FeatureId $featureId
|
||||
Write-Host ""
|
||||
$step++
|
||||
}
|
||||
}
|
||||
@@ -324,8 +347,11 @@ function Invoke-AllChanges {
|
||||
throw "Win11Debloat is running as the SYSTEM account. Use the '-User' or '-Sysprep' parameter to target a specific user."
|
||||
}
|
||||
|
||||
$script:RegistryImportFailures = 0
|
||||
$script:AppRemovalFailures = 0
|
||||
$script:FeatureFailures = 0
|
||||
$script:ApplyFeatureFailures = 0
|
||||
$script:UndoFeatureFailures = 0
|
||||
$script:PrerequisiteFailures = 0
|
||||
$script:AppRemovalVerificationUnavailable = $false
|
||||
|
||||
# ---- Gather work items ----
|
||||
@@ -405,7 +431,11 @@ function Invoke-AllChanges {
|
||||
}
|
||||
else {
|
||||
Write-Host "> Creating a system restore point..."
|
||||
Invoke-SystemRestorePoint
|
||||
$restorePointSucceeded = Invoke-SystemRestorePoint
|
||||
if (-not $restorePointSucceeded) {
|
||||
if ($script:CancelRequested) { return }
|
||||
$script:PrerequisiteFailures++
|
||||
}
|
||||
Write-Host ""
|
||||
}
|
||||
}
|
||||
@@ -429,19 +459,30 @@ function Invoke-AllChanges {
|
||||
}
|
||||
|
||||
# ================================================================
|
||||
# Final: Report registry import and app removal failures
|
||||
# Final: Report failures
|
||||
# ================================================================
|
||||
if ($script:RegistryImportFailures -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Warning "$($script:RegistryImportFailures) registry import change(s) failed. See output above for details."
|
||||
}
|
||||
|
||||
if ($script:AppRemovalFailures -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Warning "$($script:AppRemovalFailures) app removal(s) failed. See output above for details."
|
||||
}
|
||||
|
||||
if ($script:ApplyFeatureFailures -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Warning "$($script:ApplyFeatureFailures) feature change(s) failed to apply. See output above for details."
|
||||
}
|
||||
|
||||
if ($script:UndoFeatureFailures -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Warning "$($script:UndoFeatureFailures) feature change(s) failed to undo. See output above for details."
|
||||
}
|
||||
|
||||
if ($script:PrerequisiteFailures -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Warning "$($script:PrerequisiteFailures) requested prerequisite(s) could not be completed. Changes continued at your request."
|
||||
}
|
||||
|
||||
if ($script:AppRemovalVerificationUnavailable) {
|
||||
Write-Host ""
|
||||
Write-Warning "Unable to verify if all apps were uninstalled successfully."
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,18 @@
|
||||
function Invoke-SystemRestorePoint {
|
||||
$SysRestore = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore" -Name "RPSessionInterval"
|
||||
$failed = $false
|
||||
$isSilent = ($script:Params -and $script:Params.ContainsKey('Silent')) -or $script:Silent
|
||||
|
||||
if ($SysRestore.RPSessionInterval -eq 0) {
|
||||
try {
|
||||
$SysRestore = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore" -Name "RPSessionInterval" -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-Host "Error: Unable to determine whether System Restore is enabled: $($_.Exception.Message)" -ForegroundColor Red
|
||||
$failed = $true
|
||||
}
|
||||
|
||||
if (-not $failed -and $SysRestore.RPSessionInterval -eq 0) {
|
||||
# In GUI mode, skip the prompt and just try to enable it
|
||||
if ($script:GuiWindow -or $Silent -or $( Read-Host -Prompt "System restore is disabled, would you like to enable it and create a restore point? (y/n)") -eq 'y') {
|
||||
if ($script:GuiWindow -or $isSilent -or $( Read-Host -Prompt "System restore is disabled, would you like to enable it and create a restore point? (y/n)") -eq 'y') {
|
||||
try {
|
||||
$enableResult = Invoke-NonBlocking -TimeoutSeconds 90 -ScriptBlock {
|
||||
try {
|
||||
@@ -26,7 +34,6 @@ function Invoke-SystemRestorePoint {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host ""
|
||||
$failed = $true
|
||||
}
|
||||
}
|
||||
@@ -79,17 +86,20 @@ function Invoke-SystemRestorePoint {
|
||||
|
||||
if ($result -ne "Yes") {
|
||||
$script:CancelRequested = $true
|
||||
return
|
||||
return $false
|
||||
}
|
||||
}
|
||||
elseif (-not $Silent) {
|
||||
elseif (-not $isSilent) {
|
||||
Write-Host "Failed to create a system restore point. Do you want to continue without a restore point? (y/n)" -ForegroundColor Yellow
|
||||
if ($( Read-Host ) -ne 'y') {
|
||||
$script:CancelRequested = $true
|
||||
return
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Warning: Continuing without restore point" -ForegroundColor Yellow
|
||||
return $false
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
@@ -29,8 +29,7 @@ function Replace-StartMenuForAllUsers {
|
||||
# Check if template bin file exists
|
||||
if (-not (Test-Path $startMenuTemplate)) {
|
||||
Write-Host "Error: Unable to clear start menu, start2.bin file missing from script folder" -ForegroundColor Red
|
||||
Write-Host ""
|
||||
return
|
||||
return $false
|
||||
}
|
||||
|
||||
# Get path to start menu file for all users
|
||||
@@ -38,8 +37,11 @@ function Replace-StartMenuForAllUsers {
|
||||
$usersStartMenuPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue
|
||||
|
||||
# Go through all users and replace the start menu file
|
||||
$success = $true
|
||||
ForEach ($startMenuPath in $usersStartMenuPaths) {
|
||||
Replace-StartMenu -startMenuBinFile "$($startMenuPath.Fullname)\start2.bin" -startMenuTemplate $startMenuTemplate
|
||||
if (-not (Replace-StartMenu -startMenuBinFile "$($startMenuPath.Fullname)\start2.bin" -startMenuTemplate $startMenuTemplate)) {
|
||||
$success = $false
|
||||
}
|
||||
}
|
||||
|
||||
# Also replace the start menu file for the default user profile
|
||||
@@ -47,19 +49,29 @@ function Replace-StartMenuForAllUsers {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Replace Start Menu for Default user profile with template $startMenuTemplate" -ForegroundColor Cyan
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
# Create folder if it doesn't exist
|
||||
if (-not (Test-Path $defaultStartMenuPath)) {
|
||||
new-item $defaultStartMenuPath -ItemType Directory -Force | Out-Null
|
||||
Write-Host "Created LocalState folder for default user profile"
|
||||
try {
|
||||
New-Item $defaultStartMenuPath -ItemType Directory -Force -ErrorAction Stop | Out-Null
|
||||
Write-Host "Created LocalState folder for default user profile"
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to create the Default profile Start Menu directory: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Copy template to default profile
|
||||
Replace-StartMenu -startMenuBinFile "$($defaultStartMenuPath)\start2.bin" -startMenuTemplate $startMenuTemplate
|
||||
Write-Host "Replaced start menu for the default user profile"
|
||||
Write-Host ""
|
||||
if (-not (Replace-StartMenu -startMenuBinFile "$($defaultStartMenuPath)\start2.bin" -startMenuTemplate $startMenuTemplate)) {
|
||||
$success = $false
|
||||
}
|
||||
else {
|
||||
Write-Host "Replaced start menu for the default user profile"
|
||||
}
|
||||
return $success
|
||||
}
|
||||
|
||||
|
||||
@@ -98,19 +110,19 @@ function Replace-StartMenu {
|
||||
# Check if template bin file exists
|
||||
if (-not (Test-Path $startMenuTemplate)) {
|
||||
Write-Host "Error: Unable to replace start menu, template file not found" -ForegroundColor Red
|
||||
return
|
||||
return $false
|
||||
}
|
||||
|
||||
if ([IO.Path]::GetExtension($startMenuTemplate) -ne ".bin") {
|
||||
Write-Host "Error: Unable to replace start menu, template file is not a valid .bin file" -ForegroundColor Red
|
||||
return
|
||||
return $false
|
||||
}
|
||||
|
||||
$userName = Get-StartMenuUserNameFromPath -StartMenuBinFile $startMenuBinFile
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Replace Start Menu for user $userName with template $startMenuTemplate" -ForegroundColor Cyan
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
|
||||
@@ -118,20 +130,27 @@ function Replace-StartMenu {
|
||||
$startMenuDir = Split-Path $startMenuBinFile -Parent
|
||||
$backupBinFile = Join-Path $startMenuDir $backupFileName
|
||||
|
||||
if (Test-Path $startMenuBinFile) {
|
||||
# Backup current start menu file
|
||||
Copy-Item -Path $startMenuBinFile -Destination $backupBinFile -Force
|
||||
Write-Verbose "Start menu backup for user $userName saved to $backupFileName"
|
||||
}
|
||||
else {
|
||||
Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow
|
||||
New-Item -ItemType File -Path $startMenuBinFile -Force
|
||||
}
|
||||
try {
|
||||
if (Test-Path $startMenuBinFile) {
|
||||
# Backup current start menu file
|
||||
Copy-Item -Path $startMenuBinFile -Destination $backupBinFile -Force -ErrorAction Stop
|
||||
Write-Verbose "Start menu backup for user $userName saved to $backupFileName"
|
||||
}
|
||||
else {
|
||||
Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow
|
||||
New-Item -ItemType File -Path $startMenuBinFile -Force -ErrorAction Stop | Out-Null
|
||||
}
|
||||
|
||||
# Copy template file
|
||||
Copy-Item -Path $startMenuTemplate -Destination $startMenuBinFile -Force
|
||||
# Copy template file
|
||||
Copy-Item -Path $startMenuTemplate -Destination $startMenuBinFile -Force -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to replace Start Menu for user ${userName}: $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host "Replaced start menu for user $userName"
|
||||
return $true
|
||||
}
|
||||
|
||||
<#
|
||||
@@ -447,4 +466,4 @@ function Restore-StartMenuForAllUsers {
|
||||
}
|
||||
|
||||
return $results
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,36 @@
|
||||
DisableStoreSearchSuggestionsForAllUsers
|
||||
#>
|
||||
function Set-StoreSearchSuggestionsDisabledForAllUsers {
|
||||
$success = $true
|
||||
$processedProfiles = 0
|
||||
|
||||
# Get path to Store app database for all users
|
||||
$userPathString = Get-UserDirectory -userName "*" -fileName "AppData\Local\Packages"
|
||||
$usersStoreDbPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue
|
||||
|
||||
# Go through all users and disable start search suggestions
|
||||
foreach ($storeDbPath in $usersStoreDbPaths) {
|
||||
Set-StoreSearchSuggestionsDisabled -StoreAppsDatabase ($storeDbPath.FullName + "\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db")
|
||||
$processedProfiles++
|
||||
if (-not (Set-StoreSearchSuggestionsDisabled -StoreAppsDatabase ($storeDbPath.FullName + "\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db"))) {
|
||||
$success = $false
|
||||
}
|
||||
}
|
||||
|
||||
# Also disable start search suggestions for the default user profile
|
||||
$defaultStoreDbPath = Get-StoreAppsDatabasePathForUser -UserName "Default"
|
||||
if ($defaultStoreDbPath) {
|
||||
Set-StoreSearchSuggestionsDisabled -StoreAppsDatabase $defaultStoreDbPath
|
||||
$processedProfiles++
|
||||
if (-not (Set-StoreSearchSuggestionsDisabled -StoreAppsDatabase $defaultStoreDbPath)) {
|
||||
$success = $false
|
||||
}
|
||||
}
|
||||
|
||||
if ($processedProfiles -eq 0) {
|
||||
Write-Warning 'Unable to disable Microsoft Store search suggestions because no target user profiles could be resolved.'
|
||||
return $false
|
||||
}
|
||||
|
||||
return $success
|
||||
}
|
||||
|
||||
|
||||
@@ -56,24 +72,22 @@ function Set-StoreSearchSuggestionsDisabled {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Disable Microsoft Store search suggestions for user $userName by restricting access to ${StoreAppsDatabase}" -ForegroundColor Cyan
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
# This file doesn't exist in EEA (No Store app suggestions).
|
||||
if (-not (Test-Path -Path $StoreAppsDatabase))
|
||||
{
|
||||
Write-Host "Unable to find Store app database for user $userName, creating it now to prevent Windows from creating it later..." -ForegroundColor Yellow
|
||||
try {
|
||||
# This file doesn't exist in EEA (No Store app suggestions).
|
||||
if (-not (Test-Path -Path $StoreAppsDatabase)) {
|
||||
Write-Host "Unable to find Store app database for user $userName, creating it now to prevent Windows from creating it later..." -ForegroundColor Yellow
|
||||
|
||||
$storeDbDir = Split-Path -Path $StoreAppsDatabase -Parent
|
||||
$storeDbDir = Split-Path -Path $StoreAppsDatabase -Parent
|
||||
if (-not (Test-Path -Path $storeDbDir)) {
|
||||
New-Item -Path $storeDbDir -ItemType Directory -Force -ErrorAction Stop | Out-Null
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $storeDbDir)) {
|
||||
New-Item -Path $storeDbDir -ItemType Directory -Force | Out-Null
|
||||
New-Item -Path $StoreAppsDatabase -ItemType File -Force -ErrorAction Stop | Out-Null
|
||||
}
|
||||
|
||||
New-Item -Path $StoreAppsDatabase -ItemType File -Force | Out-Null
|
||||
}
|
||||
|
||||
try {
|
||||
$AccountSid = [System.Security.Principal.SecurityIdentifier]::new('S-1-1-0') # 'EVERYONE' group
|
||||
$Acl = Get-Acl -Path $StoreAppsDatabase -ErrorAction Stop
|
||||
$Ace = [System.Security.AccessControl.FileSystemAccessRule]::new($AccountSid, 'FullControl', 'Deny')
|
||||
@@ -82,10 +96,11 @@ function Set-StoreSearchSuggestionsDisabled {
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to restrict ACL for store database '$StoreAppsDatabase': $($_.Exception.Message)"
|
||||
return
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host "Disabled Microsoft Store search suggestions for user $userName"
|
||||
return $true
|
||||
}
|
||||
|
||||
<#
|
||||
@@ -102,20 +117,36 @@ function Set-StoreSearchSuggestionsDisabled {
|
||||
EnableStoreSearchSuggestionsForAllUsers
|
||||
#>
|
||||
function Set-StoreSearchSuggestionsEnabledForAllUsers {
|
||||
$success = $true
|
||||
$processedProfiles = 0
|
||||
|
||||
# Get path to Store app database for all users
|
||||
$userPathString = Get-UserDirectory -userName "*" -fileName "AppData\Local\Packages"
|
||||
$usersStoreDbPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue
|
||||
|
||||
# Go through all users and re-enable start search suggestions
|
||||
foreach ($storeDbPath in $usersStoreDbPaths) {
|
||||
Set-StoreSearchSuggestionsEnabled -StoreAppsDatabase ($storeDbPath.FullName + "\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db")
|
||||
$processedProfiles++
|
||||
if (-not (Set-StoreSearchSuggestionsEnabled -StoreAppsDatabase ($storeDbPath.FullName + "\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db"))) {
|
||||
$success = $false
|
||||
}
|
||||
}
|
||||
|
||||
# Also re-enable for the default user profile
|
||||
$defaultStoreDbPath = Get-StoreAppsDatabasePathForUser -UserName "Default"
|
||||
if ($defaultStoreDbPath) {
|
||||
Set-StoreSearchSuggestionsEnabled -StoreAppsDatabase $defaultStoreDbPath
|
||||
$processedProfiles++
|
||||
if (-not (Set-StoreSearchSuggestionsEnabled -StoreAppsDatabase $defaultStoreDbPath)) {
|
||||
$success = $false
|
||||
}
|
||||
}
|
||||
|
||||
if ($processedProfiles -eq 0) {
|
||||
Write-Warning 'Unable to re-enable Microsoft Store search suggestions because no target user profiles could be resolved.'
|
||||
return $false
|
||||
}
|
||||
|
||||
return $success
|
||||
}
|
||||
|
||||
<#
|
||||
@@ -145,23 +176,31 @@ function Set-StoreSearchSuggestionsEnabled {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Re-enable Microsoft Store search suggestions for user $userName by restoring access to ${StoreAppsDatabase}" -ForegroundColor Cyan
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $StoreAppsDatabase)) {
|
||||
Write-Host "Store app database not found for user $userName, nothing to undo"
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
# Ensure we can modify/delete the file even if restrictive ACLs were set.
|
||||
$global:LASTEXITCODE = 0
|
||||
takeown /F "$StoreAppsDatabase" /A | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "Failed to take ownership of store database '$StoreAppsDatabase' while undoing Microsoft Store search suggestions. Exit code: $LASTEXITCODE"
|
||||
return $false
|
||||
}
|
||||
icacls "$StoreAppsDatabase" /grant *S-1-5-32-544:F /C | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Warning "Failed to grant Administrators access to store database '$StoreAppsDatabase' while undoing Microsoft Store search suggestions. Exit code: $LASTEXITCODE"
|
||||
return $false
|
||||
}
|
||||
|
||||
$everyoneSid = [System.Security.Principal.SecurityIdentifier]::new('S-1-1-0') # 'EVERYONE' group
|
||||
|
||||
try {
|
||||
$acl = Get-Acl -Path $StoreAppsDatabase
|
||||
$acl = Get-Acl -Path $StoreAppsDatabase -ErrorAction Stop
|
||||
$denyRules = @(
|
||||
$acl.Access | Where-Object {
|
||||
if ($_.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Deny) { return $false }
|
||||
@@ -179,7 +218,7 @@ function Set-StoreSearchSuggestionsEnabled {
|
||||
$null = $acl.RemoveAccessRuleSpecific($denyRule)
|
||||
}
|
||||
|
||||
Set-Acl -Path $StoreAppsDatabase -AclObject $acl | Out-Null
|
||||
Set-Acl -Path $StoreAppsDatabase -AclObject $acl -ErrorAction Stop | Out-Null
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to normalize ACL for store database '$StoreAppsDatabase': $($_.Exception.Message)"
|
||||
@@ -188,9 +227,11 @@ function Set-StoreSearchSuggestionsEnabled {
|
||||
try {
|
||||
Remove-Item -Path $StoreAppsDatabase -Force -ErrorAction Stop
|
||||
Write-Host "Re-enabled Microsoft Store search suggestions for user $userName"
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
throw "Failed to remove '$StoreAppsDatabase' while undoing Microsoft Store search suggestions for user $userName. $($_.Exception.Message)"
|
||||
Write-Warning "Failed to remove '$StoreAppsDatabase' while undoing Microsoft Store search suggestions for user $userName. $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,15 +39,17 @@ function Disable-TelemetryScheduledTasks {
|
||||
Write-Host "> Disabling telemetry scheduled tasks..."
|
||||
$tasks = Get-TelemetryScheduledTasks
|
||||
|
||||
$success = $true
|
||||
foreach ($task in $tasks) {
|
||||
if ($script:CancelRequested) { return }
|
||||
if ($script:CancelRequested) { return $false }
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Disable Scheduled Task: $($task.Path)$($task.Name)" -ForegroundColor Cyan
|
||||
continue
|
||||
}
|
||||
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
try {
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
param($path, $name)
|
||||
Import-Module ScheduledTasks -ErrorAction SilentlyContinue
|
||||
$taskObj = Get-ScheduledTask -TaskPath $path -TaskName $name -ErrorAction SilentlyContinue
|
||||
@@ -64,17 +66,24 @@ function Disable-TelemetryScheduledTasks {
|
||||
}
|
||||
}
|
||||
return @{ Success = $true; Status = 'AlreadyDisabled' }
|
||||
} -ArgumentList @($task.Path, $task.Name)
|
||||
} -ArgumentList @($task.Path, $task.Name)
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disable Scheduled Task: $($task.Path)$($task.Name) - $($_.Exception.Message)"
|
||||
$success = $false
|
||||
continue
|
||||
}
|
||||
|
||||
switch ($result.Status) {
|
||||
'Disabled' { Write-Host "Disabled Scheduled Task: $($task.Path)$($task.Name)" }
|
||||
'AlreadyDisabled' { Write-Host "Scheduled Task $($task.Path)$($task.Name) is already disabled" -ForegroundColor DarkGray }
|
||||
'NotFound' { Write-Host "Scheduled Task $($task.Path)$($task.Name) not found" -ForegroundColor DarkGray }
|
||||
'Error' { Write-Host "Failed to disable Scheduled Task: $($task.Path)$($task.Name) - $($result.Error)" -ForegroundColor Yellow }
|
||||
'Error' { Write-Host "Failed to disable Scheduled Task: $($task.Path)$($task.Name) - $($result.Error)" -ForegroundColor Yellow; $success = $false }
|
||||
default { Write-Warning "Unable to determine the result of disabling Scheduled Task: $($task.Path)$($task.Name)."; $success = $false }
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
return $success
|
||||
}
|
||||
|
||||
<#
|
||||
@@ -93,15 +102,17 @@ function Enable-TelemetryScheduledTasks {
|
||||
Write-Host "> Enabling telemetry scheduled tasks..."
|
||||
$tasks = Get-TelemetryScheduledTasks
|
||||
|
||||
$success = $true
|
||||
foreach ($task in $tasks) {
|
||||
if ($script:CancelRequested) { return }
|
||||
if ($script:CancelRequested) { return $false }
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Enable Scheduled Task: $($task.Path)$($task.Name)" -ForegroundColor Cyan
|
||||
continue
|
||||
}
|
||||
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
try {
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
param($path, $name)
|
||||
Import-Module ScheduledTasks -ErrorAction SilentlyContinue
|
||||
$taskObj = Get-ScheduledTask -TaskPath $path -TaskName $name -ErrorAction SilentlyContinue
|
||||
@@ -118,15 +129,22 @@ function Enable-TelemetryScheduledTasks {
|
||||
}
|
||||
}
|
||||
return @{ Success = $true; Status = 'AlreadyEnabled' }
|
||||
} -ArgumentList @($task.Path, $task.Name)
|
||||
} -ArgumentList @($task.Path, $task.Name)
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to enable Scheduled Task: $($task.Path)$($task.Name) - $($_.Exception.Message)"
|
||||
$success = $false
|
||||
continue
|
||||
}
|
||||
|
||||
switch ($result.Status) {
|
||||
'Enabled' { Write-Host "Enabled Scheduled Task: $($task.Path)$($task.Name)" }
|
||||
'AlreadyEnabled' { Write-Host "Scheduled Task $($task.Path)$($task.Name) is already enabled." -ForegroundColor DarkGray }
|
||||
'NotFound' { Write-Host "Scheduled Task $($task.Path)$($task.Name) not found." -ForegroundColor DarkGray }
|
||||
'Error' { Write-Host "Failed to enable Scheduled Task: $($task.Path)$($task.Name) - $($result.Error)" -ForegroundColor Yellow }
|
||||
'Error' { Write-Host "Failed to enable Scheduled Task: $($task.Path)$($task.Name) - $($result.Error)" -ForegroundColor Yellow; $success = $false }
|
||||
default { Write-Warning "Unable to determine the result of enabling Scheduled Task: $($task.Path)$($task.Name)."; $success = $false }
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
return $success
|
||||
}
|
||||
|
||||
@@ -6,19 +6,42 @@ function Enable-WindowsFeature {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Enable Windows feature: $FeatureName" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
param($name)
|
||||
Enable-WindowsOptionalFeature -Online -FeatureName $name -All -NoRestart
|
||||
} -ArgumentList $FeatureName
|
||||
|
||||
$dismResult = @($result) | Where-Object { $_ -is [Microsoft.Dism.Commands.ImageObject] }
|
||||
if ($dismResult) {
|
||||
Write-Host ($dismResult | Out-String).Trim()
|
||||
try {
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
param($name)
|
||||
try {
|
||||
$output = Enable-WindowsOptionalFeature -Online -FeatureName $name -All -NoRestart -ErrorAction Stop
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
Output = if ($output) { ($output | Out-String).Trim() } else { $null }
|
||||
Error = $null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return [PSCustomObject]@{
|
||||
Success = $false
|
||||
Output = $null
|
||||
Error = $_.Exception.Message
|
||||
}
|
||||
}
|
||||
} -ArgumentList $FeatureName
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to enable Windows feature '$FeatureName': $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
if (-not $result -or -not $result.Success) {
|
||||
$details = if ($result -and $result.Error) { ": $($result.Error)" } else { '' }
|
||||
Write-Warning "Failed to enable Windows feature '$FeatureName'$details"
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($result.Output) { Write-Host $result.Output }
|
||||
return $true
|
||||
}
|
||||
|
||||
# Disables a Windows optional feature and pipes its output to the console
|
||||
@@ -29,19 +52,42 @@ function Disable-WindowsFeature {
|
||||
|
||||
if ($script:Params.ContainsKey("WhatIf")) {
|
||||
Write-Host "[WhatIf] Disable Windows feature: $FeatureName" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
return
|
||||
return $true
|
||||
}
|
||||
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
param($name)
|
||||
Disable-WindowsOptionalFeature -Online -FeatureName $name -NoRestart
|
||||
} -ArgumentList $FeatureName
|
||||
|
||||
$dismResult = @($result) | Where-Object { $_ -is [Microsoft.Dism.Commands.ImageObject] }
|
||||
if ($dismResult) {
|
||||
Write-Host ($dismResult | Out-String).Trim()
|
||||
try {
|
||||
$result = Invoke-NonBlocking -ScriptBlock {
|
||||
param($name)
|
||||
try {
|
||||
$output = Disable-WindowsOptionalFeature -Online -FeatureName $name -NoRestart -ErrorAction Stop
|
||||
return [PSCustomObject]@{
|
||||
Success = $true
|
||||
Output = if ($output) { ($output | Out-String).Trim() } else { $null }
|
||||
Error = $null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
return [PSCustomObject]@{
|
||||
Success = $false
|
||||
Output = $null
|
||||
Error = $_.Exception.Message
|
||||
}
|
||||
}
|
||||
} -ArgumentList $FeatureName
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disable Windows feature '$FeatureName': $($_.Exception.Message)"
|
||||
return $false
|
||||
}
|
||||
|
||||
if (-not $result -or -not $result.Success) {
|
||||
$details = if ($result -and $result.Error) { ": $($result.Error)" } else { '' }
|
||||
Write-Warning "Failed to disable Windows feature '$FeatureName'$details"
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($result.Output) { Write-Host $result.Output }
|
||||
return $true
|
||||
}
|
||||
|
||||
function Test-WindowsOptionalFeatureEnabled {
|
||||
@@ -58,4 +104,4 @@ function Test-WindowsOptionalFeatureEnabled {
|
||||
}
|
||||
|
||||
return ($feature.State -eq 'Enabled')
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user