From 924c192ca55d5cbd2e99f0d80773992ca1666b7b Mon Sep 17 00:00:00 2001 From: Jeffrey <9938813+Raphire@users.noreply.github.com> Date: Wed, 20 May 2026 16:29:06 +0200 Subject: [PATCH] Add Registry write fall-back in case applying registry file fails (#592) * Continue on registry failures and show details after execution * Temporarily remove DisableSearchHighlights and DisableSearchHistory settings * Remove widget-related registry changes as they're no longer required for disabling widgets * Update tooltip for DisableTelemetry feature to clarify impact on Windows Insider updates --- Config/Features.json | 32 +-- Regfiles/Disable_Widgets_Service.reg | Bin 588 -> 0 bytes Regfiles/Sysprep/Disable_Widgets_Service.reg | Bin 588 -> 0 bytes Regfiles/Undo/Enable_Widgets_Service.reg | Bin 560 -> 0 bytes Scripts/Features/ExecuteChanges.ps1 | 18 +- Scripts/Features/ImportRegistryFile.ps1 | 114 +++++----- Scripts/GUI/Show-ApplyModal.ps1 | 9 +- Scripts/Helpers/ApplyRegistryRegFile.ps1 | 223 +++++++++++++++++++ Win11Debloat.ps1 | 7 +- 9 files changed, 312 insertions(+), 91 deletions(-) delete mode 100644 Regfiles/Disable_Widgets_Service.reg delete mode 100644 Regfiles/Sysprep/Disable_Widgets_Service.reg delete mode 100644 Regfiles/Undo/Enable_Widgets_Service.reg create mode 100644 Scripts/Helpers/ApplyRegistryRegFile.ps1 diff --git a/Config/Features.json b/Config/Features.json index 9f90933..0914012 100644 --- a/Config/Features.json +++ b/Config/Features.json @@ -366,7 +366,7 @@ { "FeatureId": "DisableTelemetry", "Label": "Disable telemetry, tracking & targeted ads", - "ToolTip": "This setting disables telemetry, diagnostic data collection, activity history, app-launch tracking, targeted ads and more. It limits the data that is sent to Microsoft about your device and usage.", + "ToolTip": "This setting disables telemetry, diagnostic data collection, activity history, app-launch tracking, targeted ads and more. It limits the data that is sent to Microsoft about your device and usage. If you are a Windows Insider, updates may be blocked until optional diagnostic data collection is turned back on.", "Category": "Privacy & Suggested Content", "RegistryKey": "Disable_Telemetry.reg", "ApplyText": "Disabling telemetry, diagnostic data, activity history, app-launch tracking and targeted ads...", @@ -601,28 +601,6 @@ "MinVersion": 22621, "MaxVersion": null }, - { - "FeatureId": "DisableSearchHighlights", - "Label": "Disable Search Highlights in the taskbar search box", - "ToolTip": "This will turn off Search Highlights, which shows dynamically curated branded content and trending topics in the Windows search box on the taskbar.", - "Category": "Start Menu & Search", - "RegistryKey": "Disable_Search_Highlights.reg", - "ApplyText": "Disabling Search Highlights in the Windows search box...", - "RegistryUndoKey": "Enable_Search_Highlights.reg", - "MinVersion": 22621, - "MaxVersion": null - }, - { - "FeatureId": "DisableSearchHistory", - "Label": "Disable local Windows Search history", - "ToolTip": "This setting disables local search history in Windows Search. This does not affect web search history or the search history saved in Microsoft Edge.", - "Category": "Start Menu & Search", - "RegistryKey": "Disable_Search_History.reg", - "ApplyText": "Disabling search history...", - "RegistryUndoKey": "Enable_Search_History.reg", - "MinVersion": null, - "MaxVersion": null - }, { "FeatureId": "DisableSettings365Ads", "Label": "Hide Microsoft 365 Copilot ads in Settings Home", @@ -878,12 +856,12 @@ { "FeatureId": "DisableWidgets", "Label": "Disable widgets on the taskbar & lock screen", - "ToolTip": "This will disable the widgets features in Windows, including the widgets button on the taskbar and the widgets that can appear on the lock screen. This feature uses policies, which will lock down certain settings.", + "ToolTip": "This will disable the widgets features in Windows, including the widgets button on the taskbar and the widgets that can appear on the lock screen.", "Category": "Taskbar", "Priority": 4, - "RegistryKey": "Disable_Widgets_Service.reg", - "ApplyText": "Disabling widgets on the taskbar & lock screen...", - "RegistryUndoKey": "Enable_Widgets_Service.reg", + "RegistryKey": null, + "ApplyText": null, + "RegistryUndoKey": null, "MinVersion": null, "MaxVersion": null }, diff --git a/Regfiles/Disable_Widgets_Service.reg b/Regfiles/Disable_Widgets_Service.reg deleted file mode 100644 index 30bddcb0ecb622f8fb6e56886ed4612191ca4f0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 588 zcmbV|&1wQc5QOV2WL+34_`Nle4$MxazsEaz)PLyX7iWWGVgLXD diff --git a/Regfiles/Sysprep/Disable_Widgets_Service.reg b/Regfiles/Sysprep/Disable_Widgets_Service.reg deleted file mode 100644 index 30bddcb0ecb622f8fb6e56886ed4612191ca4f0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 588 zcmbV|&1wQc5QOV2WL+34_`Nle4$MxazsEaz)PLyX7iWWGVgLXD diff --git a/Regfiles/Undo/Enable_Widgets_Service.reg b/Regfiles/Undo/Enable_Widgets_Service.reg deleted file mode 100644 index 05272bddc018870325fbee2a0c1b1c2ae6b87bb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 560 zcmbV}QA+|*5QWck(0^EbFYKY0q6e`gDJ@JAaYMFT?ZUFc-A(b&Yu`+yFF_HPyZ6pH zbLX6y-EBt-DmBwmYgI}#P_6}7rCL!NkQ>-Qv?NaS3?E?waY=lxQ#Dz~I`Vz#FG^*4 zRY7h--9Uu}_u3UIudo7r<7V$By44*zV@-9h2esAWoT|s!hU=;io9YR!qoGFN14nvZ zCE}%G&rwMA$gOaELxl{NnX5;*8H#h_4>BybPi%d+d Disabling widgets on the taskbar & lock screen..." + # Stop widgets related processes before removing the app packages to prevent potential issues + Get-Process *Widget* | Stop-Process + + RemoveApps 'Microsoft.StartExperiencesApp','MicrosoftWindows.Client.WebExperience','Microsoft.WidgetsPlatformRuntime' + } "EnableWindowsSandbox" { Write-Host "> Enabling Windows Sandbox..." EnableWindowsFeature "Containers-DisposableClientVM" @@ -138,6 +141,8 @@ function ExecuteParameter { # Executes all selected parameters/features function ExecuteAllChanges { + $script:RegistryImportFailures = 0 + # Build list of actionable parameters (skip control params and data-only params) $actionableKeys = @() foreach ($paramKey in $script:Params.Keys) { @@ -216,4 +221,9 @@ function ExecuteAllChanges { ExecuteParameter -paramKey $paramKey } + + if ($script:RegistryImportFailures -gt 0) { + Write-Host "" + Write-Host "$($script:RegistryImportFailures) registry import change(s) failed. See output above for details." -ForegroundColor Yellow + } } \ No newline at end of file diff --git a/Scripts/Features/ImportRegistryFile.ps1 b/Scripts/Features/ImportRegistryFile.ps1 index 215e163..8df2cbb 100644 --- a/Scripts/Features/ImportRegistryFile.ps1 +++ b/Scripts/Features/ImportRegistryFile.ps1 @@ -8,33 +8,44 @@ function ImportRegistryFile { Write-Host $message $usesOfflineHive = $script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User") - $regFilePath = if ($usesOfflineHive) { - "$script:RegfilesPath\Sysprep\$path" + $regFileDirectory = if ($usesOfflineHive) { + Join-Path $script:RegfilesPath "Sysprep" } else { - "$script:RegfilesPath\$path" + $script:RegfilesPath } + $regFilePath = Join-Path $regFileDirectory $path 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 } - # Reset exit code before running reg.exe for reliable success detection - $global:LASTEXITCODE = 0 + $regResult = $null + $offlineHiveLoaded = $false - if ($usesOfflineHive) { - # Sysprep targets Default user, User targets the specified user - $hiveDatPath = if ($script:Params.ContainsKey("Sysprep")) { - GetUserDirectory -userName "Default" -fileName "NTUSER.DAT" - } else { - GetUserDirectory -userName $script:Params.Item("User") -fileName "NTUSER.DAT" + try { + if ($usesOfflineHive) { + # Sysprep targets Default user, User targets the specified user + $targetUserName = if ($script:Params.ContainsKey("Sysprep")) { "Default" } else { $script:Params.Item("User") } + $hiveDatPath = GetUserDirectory -userName $targetUserName -fileName "NTUSER.DAT" + + $global:LASTEXITCODE = 0 + reg load "HKU\Default" $hiveDatPath | Out-Null + $loadExitCode = $LASTEXITCODE + + if ($loadExitCode -ne 0) { + throw "Failed importing registry file '$path'. Offline hive load failed: Failed to load user hive at '$hiveDatPath' (exit code: $loadExitCode)" + } + + $offlineHiveLoaded = $true } $regResult = Invoke-NonBlocking -ScriptBlock { - param($hivePath, $targetRegFilePath) + param($targetRegFilePath) $result = @{ Output = @() ExitCode = 0 @@ -43,13 +54,6 @@ function ImportRegistryFile { try { $global:LASTEXITCODE = 0 - reg load "HKU\Default" $hivePath | Out-Null - $loadExitCode = $LASTEXITCODE - - if ($loadExitCode -ne 0) { - throw "Failed to load user hive at '$hivePath' (exit code: $loadExitCode)" - } - $output = reg import $targetRegFilePath 2>&1 $importExitCode = $LASTEXITCODE @@ -66,52 +70,50 @@ function ImportRegistryFile { $result.Error = $_.Exception.Message $result.ExitCode = if ($LASTEXITCODE -ne 0) { $LASTEXITCODE } else { 1 } } - finally { - $global:LASTEXITCODE = 0 - reg unload "HKU\Default" | Out-Null - $unloadExitCode = $LASTEXITCODE - if ($unloadExitCode -ne 0 -and -not $result.Error) { - $result.Error = "Failed to unload registry hive HKU\Default (exit code: $unloadExitCode)" - $result.ExitCode = $unloadExitCode - } - } return $result - } -ArgumentList @($hiveDatPath, $regFilePath) - } - else { - $regResult = Invoke-NonBlocking -ScriptBlock { - param($targetRegFilePath) - $global:LASTEXITCODE = 0 - $output = reg import $targetRegFilePath 2>&1 - return @{ Output = @($output); ExitCode = $LASTEXITCODE; Error = $null } } -ArgumentList $regFilePath - } - $regOutput = @($regResult.Output) - $hasSuccess = ($regResult.ExitCode -eq 0) -and -not $regResult.Error - - if ($regOutput) { - foreach ($line in $regOutput) { - $lineText = if ($line -is [System.Management.Automation.ErrorRecord]) { $line.Exception.Message } else { $line.ToString() } - if ($lineText -and $lineText.Length -gt 0) { - if ($hasSuccess) { - Write-Host $lineText - } - else { - Write-Host $lineText -ForegroundColor Red + $regOutput = @($regResult.Output) + $hasSuccess = ($regResult.ExitCode -eq 0) -and -not $regResult.Error + + if ($regOutput) { + foreach ($line in $regOutput) { + $lineText = if ($line -is [System.Management.Automation.ErrorRecord]) { $line.Exception.Message } else { $line.ToString() } + if ($lineText -and $lineText.Length -gt 0) { + if ($hasSuccess) { + Write-Host $lineText + } + else { + Write-Host $lineText -ForegroundColor Red + } } } } - } - if (-not $hasSuccess) { - $details = if ($regResult.Error) { $regResult.Error } else { "Exit code: $($regResult.ExitCode)" } - $errorMessage = "Failed importing registry file '$path'. $details" - Write-Host $errorMessage -ForegroundColor Red + 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 $regFilePath + Write-Host "Fallback import succeeded for '$path'." -ForegroundColor Yellow + } + Write-Host "" - throw $errorMessage } + catch { + $script:RegistryImportFailures++ + Write-Host $_.Exception.Message -ForegroundColor Red + Write-Host "" + } + finally { + if ($offlineHiveLoaded) { + $global:LASTEXITCODE = 0 + reg unload "HKU\Default" | Out-Null + $unloadExitCode = $LASTEXITCODE - Write-Host "" + if ($unloadExitCode -ne 0) { + Write-Warning "Failed to unload registry hive HKU\Default after importing '$path' (exit code: $unloadExitCode)" + } + } + } } \ No newline at end of file diff --git a/Scripts/GUI/Show-ApplyModal.ps1 b/Scripts/GUI/Show-ApplyModal.ps1 index 75ea5a9..30bc6ca 100644 --- a/Scripts/GUI/Show-ApplyModal.ps1 +++ b/Scripts/GUI/Show-ApplyModal.ps1 @@ -123,6 +123,8 @@ function Show-ApplyModal { $applyWindow.Dispatcher.BeginInvoke([System.Windows.Threading.DispatcherPriority]::Background, [action]{ try { ExecuteAllChanges + + $registryImportFailureCount = [int]$script:RegistryImportFailures # Restart explorer if requested if ($RestartExplorer -and -not $script:CancelRequested) { @@ -139,7 +141,7 @@ function Show-ApplyModal { Write-Host "" if ($script:CancelRequested) { Write-Host "Script execution was cancelled by the user. Some changes may not have been applied." - } else { + } elseif ($registryImportFailureCount -eq 0) { Write-Host "All changes have been applied successfully!" } @@ -153,6 +155,11 @@ function Show-ApplyModal { $script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d")) $script:ApplyCompletionTitleEl.Text = "Cancelled" $script:ApplyCompletionMessageEl.Text = "Script execution was cancelled by the user." + } elseif ($registryImportFailureCount -gt 0) { + $script:ApplyCompletionIconEl.Text = [char]0xE7BA + $script:ApplyCompletionIconEl.Foreground = [System.Windows.Media.SolidColorBrush]::new([System.Windows.Media.ColorConverter]::ConvertFromString("#e8912d")) + $script:ApplyCompletionTitleEl.Text = "Changes Applied with Errors" + $script:ApplyCompletionMessageEl.Text = "$registryImportFailureCount registry change(s) failed. See console for details." } else { $script:ApplyCompletionTitleEl.Text = "Changes Applied" diff --git a/Scripts/Helpers/ApplyRegistryRegFile.ps1 b/Scripts/Helpers/ApplyRegistryRegFile.ps1 new file mode 100644 index 0000000..6583a24 --- /dev/null +++ b/Scripts/Helpers/ApplyRegistryRegFile.ps1 @@ -0,0 +1,223 @@ +function Get-NormalizedRegistryValueName { + param( + [AllowNull()] + $ValueName + ) + + if ([string]::IsNullOrEmpty([string]$ValueName)) { + return '' + } + + return [string]$ValueName +} + +function Convert-RegOperationToValueKind { + param( + [Parameter(Mandatory)] + $Operation + ) + + $valueName = if ([string]::IsNullOrEmpty([string]$Operation.ValueName)) { '' } else { [string]$Operation.ValueName } + $valueType = [string]$Operation.ValueType + $operationKeyPath = [string]$Operation.KeyPath + + switch ($valueType) { + 'DWord' { + $unsigned = [uint32]$Operation.ValueData + $value = [BitConverter]::ToInt32([BitConverter]::GetBytes($unsigned), 0) + return @{ Name = $valueName; Kind = [Microsoft.Win32.RegistryValueKind]::DWord; Value = $value } + } + 'String' { + return @{ Name = $valueName; Kind = [Microsoft.Win32.RegistryValueKind]::String; Value = [string]$Operation.ValueData } + } + 'Binary' { + return @{ Name = $valueName; Kind = [Microsoft.Win32.RegistryValueKind]::Binary; Value = [byte[]]$Operation.ValueData } + } + default { + throw "Unsupported value type '$valueType' while applying reg operation for '$operationKeyPath'" + } + } +} + +function Remove-RegistrySubKeyTreeIfExists { + param( + [Parameter(Mandatory)] + [Microsoft.Win32.RegistryKey]$RootKey, + [Parameter(Mandatory)] + [string]$SubKeyPath + ) + + try { + $RootKey.DeleteSubKeyTree($SubKeyPath, $false) + } + catch [System.UnauthorizedAccessException], [System.Security.SecurityException] { + throw + } + catch { + # Best-effort cleanup only; missing keys are fine. + } +} + +function Get-RegistryKeyForOperation { + param( + [Parameter(Mandatory)] + [string]$RegistryPath, + [switch]$CreateIfMissing, + [bool]$OpenKey = $true + ) + + $parts = Split-RegistryPath -path $RegistryPath + if (-not $parts) { + throw "Unsupported registry path: $RegistryPath" + } + + $rootKey = Get-RegistryRootKey -hiveName $parts.Hive + if (-not $rootKey) { + throw "Unsupported registry hive '$($parts.Hive)' in path '$RegistryPath'" + } + + $subKeyPath = $parts.SubKey + if ([string]::IsNullOrWhiteSpace($subKeyPath)) { + return [PSCustomObject]@{ RootKey = $rootKey; SubKeyPath = $null; Key = $rootKey } + } + + if (-not $OpenKey) { + return [PSCustomObject]@{ RootKey = $rootKey; SubKeyPath = $subKeyPath; Key = $null } + } + + $key = if ($CreateIfMissing) { + $rootKey.CreateSubKey($subKeyPath) + } + else { + $rootKey.OpenSubKey($subKeyPath, $true) + } + + return [PSCustomObject]@{ RootKey = $rootKey; SubKeyPath = $subKeyPath; Key = $key } +} + +function Invoke-RegistryDeleteValueOperation { + param( + [Parameter(Mandatory)] + $Operation, + [Parameter(Mandatory)] + $KeyInfo + ) + + if ($null -eq $KeyInfo.Key) { + $valueName = Get-NormalizedRegistryValueName -ValueName $Operation.ValueName + $displayValueName = if ([string]::IsNullOrEmpty($valueName)) { '(Default)' } else { $valueName } + Write-Verbose "Unable to find or open key '$($Operation.KeyPath)' and value '$displayValueName'" + return + } + + try { + $valueName = Get-NormalizedRegistryValueName -ValueName $Operation.ValueName + $KeyInfo.Key.DeleteValue($valueName, $false) + } + finally { + $KeyInfo.Key.Close() + } +} + +function Invoke-RegistrySetValueOperation { + param( + [Parameter(Mandatory)] + $Operation, + [Parameter(Mandatory)] + $KeyInfo + ) + + if ($null -eq $KeyInfo.Key) { + throw [System.UnauthorizedAccessException]::new("Unable to open or create registry key '$($Operation.KeyPath)'") + } + + try { + $setArgs = Convert-RegOperationToValueKind -Operation $Operation + $KeyInfo.Key.SetValue($setArgs.Name, $setArgs.Value, $setArgs.Kind) + } + finally { + $KeyInfo.Key.Close() + } +} + +function Write-RegistryOperationAccessDeniedWarning { + param( + [Parameter(Mandatory)] + $Operation, + [Parameter(Mandatory)] + [string]$ExceptionMessage + ) + + $keyPath = [string]$Operation.KeyPath + $operationType = [string]$Operation.OperationType + + if ($operationType -eq 'SetValue' -or $operationType -eq 'DeleteValue') { + $valueName = Get-NormalizedRegistryValueName -ValueName $Operation.ValueName + $displayValueName = if ([string]::IsNullOrEmpty($valueName)) { '(Default)' } else { $valueName } + Write-Warning "Skipping operation '$operationType' on key '$keyPath' value '$displayValueName' due to access restrictions: $ExceptionMessage" + return + } + + Write-Warning "Skipping operation '$operationType' on key '$keyPath' due to access restrictions: $ExceptionMessage" +} + +function Invoke-RegistryOperation { + param( + [Parameter(Mandatory)] + $Operation, + [Parameter(Mandatory)] + [string]$RegFilePath + ) + + $operationType = [string]$Operation.OperationType + $isSetValueOperation = $operationType -eq 'SetValue' + $isDeleteKeyOperation = $operationType -eq 'DeleteKey' + + $keyInfo = Get-RegistryKeyForOperation -RegistryPath $Operation.KeyPath -CreateIfMissing:$isSetValueOperation -OpenKey:(-not $isDeleteKeyOperation) + + switch ($operationType) { + 'DeleteKey' { + if ($null -ne $keyInfo.SubKeyPath) { + Remove-RegistrySubKeyTreeIfExists -RootKey $keyInfo.RootKey -SubKeyPath $keyInfo.SubKeyPath + } + } + 'DeleteValue' { + Invoke-RegistryDeleteValueOperation -Operation $Operation -KeyInfo $keyInfo + } + 'SetValue' { + Invoke-RegistrySetValueOperation -Operation $Operation -KeyInfo $keyInfo + } + default { + throw "Unsupported reg operation type '$($Operation.OperationType)' in '$RegFilePath'" + } + } +} + +function Invoke-RegistryOperationsFromRegFile { + param( + [Parameter(Mandatory)] + [string]$RegFilePath + ) + + $accessDeniedCount = 0 + $operations = @(Get-RegFileOperations -regFilePath $RegFilePath) + $totalOperations = $operations.Count + + foreach ($operation in $operations) { + try { + Invoke-RegistryOperation -Operation $operation -RegFilePath $RegFilePath + } + catch [System.UnauthorizedAccessException], [System.Security.SecurityException] { + $accessDeniedCount++ + Write-RegistryOperationAccessDeniedWarning -Operation $operation -ExceptionMessage $_.Exception.Message + } + } + + if ($totalOperations -gt 0 -and $accessDeniedCount -eq $totalOperations) { + throw "Registry fallback import could not apply any operations in '$RegFilePath' because all $accessDeniedCount operation(s) were blocked by access restrictions." + } + + if ($accessDeniedCount -gt 0) { + Write-Warning "Registry fallback import completed with $accessDeniedCount access-restricted operation(s) skipped in '$RegFilePath'." + } +} diff --git a/Win11Debloat.ps1 b/Win11Debloat.ps1 index c532767..44f421e 100644 --- a/Win11Debloat.ps1 +++ b/Win11Debloat.ps1 @@ -349,6 +349,7 @@ if (-not $script:WingetInstalled -and -not $Silent) { . "$PSScriptRoot/Scripts/Helpers/GetUserDirectory.ps1" . "$PSScriptRoot/Scripts/Helpers/GetUserName.ps1" . "$PSScriptRoot/Scripts/Helpers/RegistryPathHelpers.ps1" +. "$PSScriptRoot/Scripts/Helpers/ApplyRegistryRegFile.ps1" . "$PSScriptRoot/Scripts/Helpers/TestIfUserIsLoggedIn.ps1" # Threading functions @@ -401,7 +402,7 @@ else { } if ($script:Params.ContainsKey("Sysprep")) { - $defaultUserPath = GetUserDirectory -userName "Default" + GetUserDirectory -userName "Default" | Out-Null # Exit script if run in Sysprep mode on Windows 10 if ($WinVersion -lt 22000) { @@ -412,10 +413,10 @@ if ($script:Params.ContainsKey("Sysprep")) { # Ensure that target user exists, if User or AppRemovalTarget parameter was provided if ($script:Params.ContainsKey("User")) { - $userPath = GetUserDirectory -userName $script:Params.Item("User") + GetUserDirectory -userName $script:Params.Item("User") | Out-Null } if ($script:Params.ContainsKey("AppRemovalTarget")) { - $userPath = GetUserDirectory -userName $script:Params.Item("AppRemovalTarget") + GetUserDirectory -userName $script:Params.Item("AppRemovalTarget") | Out-Null } # Remove LastUsedSettings.json file if it exists and is empty