diff --git a/Assets/Features.json b/Assets/Features.json index 2fe7dcb..3e3562c 100644 --- a/Assets/Features.json +++ b/Assets/Features.json @@ -10,7 +10,7 @@ "Icon": "" }, { - "Name": "Start Menu", + "Name": "Start Menu & Search", "Icon": "" }, { @@ -166,10 +166,11 @@ "GroupId": "ClearStart", "Label": "Remove pinned apps from the start menu", "ToolTip": "This setting allows you to quickly remove all pinned apps from the start menu.", - "Category": "Start Menu", + "Category": "Start Menu & Search", + "Priority": 1, "Values": [ { - "Label": "Remove for the current user", + "Label": "Remove for the selected user", "FeatureIds": [ "ClearStart" ] @@ -397,19 +398,6 @@ "MinVersion": null, "MaxVersion": null }, - { - "FeatureId": "DisableSearchHistory", - "Label": "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": "Privacy & Suggested Content", - "Action": "Disable", - "RegistryKey": "Disable_Search_History.reg", - "ApplyText": "Disabling search history...", - "UndoAction": "Enable", - "RegistryUndoKey": "Enable_Search_History.reg", - "MinVersion": null, - "MaxVersion": null - }, { "FeatureId": "DisableLockscreenTips", "Label": "tips & tricks on the lock screen", @@ -518,7 +506,7 @@ "FeatureId": "ClearStart", "Action": "Clear Start Menu", "Label": "Remove all pinned apps from the start menu for this user only", - "Category": "Start Menu", + "Category": "Start Menu & Search", "RegistryKey": null, "ApplyText": null, "UndoAction": null, @@ -530,7 +518,7 @@ "FeatureId": "ClearStartAllUsers", "Action": "Clear Start Menu (All Users)", "Label": "Remove all pinned apps from the start menu for all existing and new users", - "Category": "Start Menu", + "Category": "Start Menu & Search", "RegistryKey": null, "ApplyText": null, "UndoAction": null, @@ -566,7 +554,7 @@ "FeatureId": "DisableStartRecommended", "Label": "recommended section in the start menu", "ToolTip": "This will hide the recommended section in the start menu, which shows recently added apps, recently opened files and app recommendations. This feature uses policies, which will lock down certain settings.", - "Category": "Start Menu", + "Category": "Start Menu & Search", "Action": "Hide", "RegistryKey": "Disable_Start_Recommended.reg", "ApplyText": "Disabling the start menu recommended section...", @@ -575,11 +563,24 @@ "MinVersion": 22621, "MaxVersion": null }, + { + "FeatureId": "DisableStartPhoneLink", + "Label": "Phone Link integration in the start menu", + "ToolTip": "This will remove the Phone Link integration in the start menu when you have a mobile device linked to your PC.", + "Category": "Start Menu & Search", + "Action": "Disable", + "RegistryKey": "Disable_Phone_Link_In_Start.reg", + "ApplyText": "Disabling the Phone Link mobile devices integration in the start menu...", + "UndoAction": "Enable", + "RegistryUndoKey": "Enable_Phone_Link_In_Start.reg", + "MinVersion": 22621, + "MaxVersion": null + }, { "FeatureId": "DisableBing", "Label": "Bing web search & Copilot integration in search", "ToolTip": "This will turn off Bing web search results and Copilot integration in the Windows search experience. This feature uses policies, which will lock down certain settings.", - "Category": "Start Menu", + "Category": "Start Menu & Search", "Action": "Disable", "RegistryKey": "Disable_Bing_Cortana_In_Search.reg", "ApplyText": "Disabling Bing web search & Copilot integration in Windows search...", @@ -588,11 +589,24 @@ "MinVersion": null, "MaxVersion": null }, + { + "FeatureId": "DisableStoreSearchSuggestions", + "Label": "Microsoft Store app suggestions in search", + "ToolTip": "This will disable the Microsoft Store app suggestions in Windows search.", + "Category": "Start Menu & Search", + "Action": "Disable", + "RegistryKey": null, + "ApplyText": null, + "UndoAction": null, + "RegistryUndoKey": null, + "MinVersion": 22621, + "MaxVersion": null + }, { "FeatureId": "DisableSearchHighlights", - "Label": "Search Highlights (dynamic/branded content in Windows search box)", + "Label": "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": "Privacy & Suggested Content", + "Category": "Start Menu & Search", "Action": "Disable", "RegistryKey": "Disable_Search_Highlights.reg", "ApplyText": "> Disabling Search Highlights in the Windows search box...", @@ -602,16 +616,16 @@ "MaxVersion": null }, { - "FeatureId": "DisableStartPhoneLink", - "Label": "Phone Link integration in the start menu", - "ToolTip": "This will remove the Phone Link integration in the start menu when you have a mobile device linked to your PC.", - "Category": "Start Menu", + "FeatureId": "DisableSearchHistory", + "Label": "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", "Action": "Disable", - "RegistryKey": "Disable_Phone_Link_In_Start.reg", - "ApplyText": "Disabling the Phone Link mobile devices integration in the start menu...", + "RegistryKey": "Disable_Search_History.reg", + "ApplyText": "Disabling search history...", "UndoAction": "Enable", - "RegistryUndoKey": "Enable_Phone_Link_In_Start.reg", - "MinVersion": 22621, + "RegistryUndoKey": "Enable_Search_History.reg", + "MinVersion": null, "MaxVersion": null }, { diff --git a/DefaultSettings.json b/DefaultSettings.json index 608464f..0954cc8 100644 --- a/DefaultSettings.json +++ b/DefaultSettings.json @@ -25,6 +25,10 @@ "Name": "DisableBing", "Value": true }, + { + "Name": "DisableStoreSearchSuggestions", + "Value": true + }, { "Name": "DisableSearchHighlights", "Value": true diff --git a/README.md b/README.md index 7c3bf08..47e8f5b 100755 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Below is an overview of the key features and functionality offered by Win11Deblo - Disable telemetry, diagnostic data, activity history, app-launch tracking & targeted ads. - Disable tips, tricks, suggestions & ads across Windows. - Disable Windows location services & app location access. -- Disable local Windows search history. +- Disable Find My Device location tracking. - Disable 'Windows Spotlight' and tips & tricks on the lock screen. - Disable 'Windows Spotlight' desktop background option. - Disable ads, suggestions and the MSN news feed in Microsoft Edge. @@ -125,12 +125,15 @@ Below is an overview of the key features and functionality offered by Win11Deblo - Disable transparency effects - Disable animations and visual effects. -#### Start Menu +#### Start Menu & Search - Remove or replace all pinned apps from start for the current user, or for all existing & new users. (W11 only) - Disable the recommended section in the start menu. (W11 only) -- Disable Bing web search & Copilot integration in Windows search. - Disable the Phone Link mobile devices integration in the start menu. (W11 only) +- Disable Bing web search & Copilot integration in Windows search. +- Disable Microsoft Store app suggestions in Windows search. (W11 only) +- Disable Search Highlights (dynamic/branded content) in the taskbar search box. (W11 only) +- Disable local Windows search history. #### Taskbar diff --git a/Scripts/AppRemoval/ForceRemoveEdge.ps1 b/Scripts/AppRemoval/ForceRemoveEdge.ps1 new file mode 100644 index 0000000..e12b708 --- /dev/null +++ b/Scripts/AppRemoval/ForceRemoveEdge.ps1 @@ -0,0 +1,57 @@ +# Forcefully removes Microsoft Edge using its uninstaller +# Credit: Based on work from loadstring1 & ave9858 +function ForceRemoveEdge { + Write-Host "> Forcefully uninstalling Microsoft Edge..." + + $regView = [Microsoft.Win32.RegistryView]::Registry32 + $hklm = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $regView) + $hklm.CreateSubKey('SOFTWARE\Microsoft\EdgeUpdateDev').SetValue('AllowUninstall', '') + + # Create stub (This somehow allows uninstalling Edge) + $edgeStub = "$env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe" + New-Item $edgeStub -ItemType Directory | Out-Null + New-Item "$edgeStub\MicrosoftEdge.exe" | Out-Null + + # Remove edge + $uninstallRegKey = $hklm.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge') + if ($null -ne $uninstallRegKey) { + Write-Host "Running uninstaller..." + $uninstallString = $uninstallRegKey.GetValue('UninstallString') + ' --force-uninstall' + Invoke-NonBlocking -ScriptBlock { + param($cmd) + Start-Process cmd.exe "/c $cmd" -WindowStyle Hidden -Wait + } -ArgumentList $uninstallString + + Write-Host "Removing leftover files..." + + $edgePaths = @( + "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk", + "$env:APPDATA\Microsoft\Internet Explorer\Quick Launch\Microsoft Edge.lnk", + "$env:APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Microsoft Edge.lnk", + "$env:APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Tombstones\Microsoft Edge.lnk", + "$env:PUBLIC\Desktop\Microsoft Edge.lnk", + "$env:USERPROFILE\Desktop\Microsoft Edge.lnk", + "$edgeStub" + ) + + foreach ($path in $edgePaths) { + if (Test-Path -Path $path) { + Remove-Item -Path $path -Force -Recurse -ErrorAction SilentlyContinue + Write-Host " Removed $path" -ForegroundColor DarkGray + } + } + + Write-Host "Cleaning up registry..." + + # Remove MS Edge from autostart + reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v "MicrosoftEdgeAutoLaunch_A9F6DCE4ABADF4F51CF45CD7129E3C6C" /f *>$null + reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v "Microsoft Edge Update" /f *>$null + reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run" /v "MicrosoftEdgeAutoLaunch_A9F6DCE4ABADF4F51CF45CD7129E3C6C" /f *>$null + reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run" /v "Microsoft Edge Update" /f *>$null + + Write-Host "Microsoft Edge was uninstalled" + } + else { + Write-Host "Unable to forcefully uninstall Microsoft Edge, uninstaller could not be found" -ForegroundColor Red + } +} \ No newline at end of file diff --git a/Scripts/AppRemoval/RemoveApps.ps1 b/Scripts/AppRemoval/RemoveApps.ps1 new file mode 100644 index 0000000..38962e0 --- /dev/null +++ b/Scripts/AppRemoval/RemoveApps.ps1 @@ -0,0 +1,111 @@ +# Removes apps specified during function call based on the target scope. +function RemoveApps { + param ( + $appslist + ) + + # Determine target from script-level params, defaulting to AllUsers + $targetUser = GetTargetUserForAppRemoval + + $appIndex = 0 + $appCount = @($appsList).Count + + Foreach ($app in $appsList) { + if ($script:CancelRequested) { + return + } + + $appIndex++ + + # Update step name and sub-progress to show which app is being removed (only for bulk removal) + if ($script:ApplySubStepCallback -and $appCount -gt 1) { + & $script:ApplySubStepCallback "Removing apps ($appIndex/$appCount)" $appIndex $appCount + } + + Write-Host "Attempting to remove $app..." + + # Use WinGet only to remove OneDrive and Edge + if (($app -eq "Microsoft.OneDrive") -or ($app -eq "Microsoft.Edge")) { + if ($script:WingetInstalled -eq $false) { + Write-Host "WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red + continue + } + + $appName = $app -replace '\.', '_' + + # Uninstall app via WinGet, or create a scheduled task to uninstall it later + if ($script:Params.ContainsKey("User")) { + ImportRegistryFile "Adding scheduled task to uninstall $app for user $(GetUserName)..." "Uninstall_$($appName).reg" + } + elseif ($script:Params.ContainsKey("Sysprep")) { + ImportRegistryFile "Adding scheduled task to uninstall $app after for new users..." "Uninstall_$($appName).reg" + } + else { + # Uninstall app via WinGet + $wingetOutput = Invoke-NonBlocking -ScriptBlock { + param($appId) + winget uninstall --accept-source-agreements --disable-interactivity --id $appId + } -ArgumentList $app + + If (($app -eq "Microsoft.Edge") -and (Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code")) { + Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red + + if ($script:GuiWindow) { + $result = Show-MessageBox -Message 'Unable to uninstall Microsoft Edge via WinGet. Would you like to forcefully uninstall it? NOT RECOMMENDED!' -Title 'Force Uninstall Microsoft Edge?' -Button 'YesNo' -Icon 'Warning' + + if ($result -eq 'Yes') { + Write-Host "" + ForceRemoveEdge + } + } + elseif ($( Read-Host -Prompt "Would you like to forcefully uninstall Microsoft Edge? NOT RECOMMENDED! (y/n)" ) -eq 'y') { + Write-Host "" + ForceRemoveEdge + } + } + } + + continue + } + + # Use Remove-AppxPackage to remove all other apps + $appPattern = '*' + $app + '*' + + try { + switch ($targetUser) { + "AllUsers" { + # Remove installed app for all existing users, and from OS image + Invoke-NonBlocking -ScriptBlock { + param($pattern) + Get-AppxPackage -Name $pattern -AllUsers | Remove-AppxPackage -AllUsers -ErrorAction Continue + Get-AppxProvisionedPackage -Online | Where-Object { $_.PackageName -like $pattern } | ForEach-Object { Remove-ProvisionedAppxPackage -Online -AllUsers -PackageName $_.PackageName } + } -ArgumentList $appPattern + } + "CurrentUser" { + # Remove installed app for current user only + Invoke-NonBlocking -ScriptBlock { + param($pattern) + Get-AppxPackage -Name $pattern | Remove-AppxPackage -ErrorAction Continue + } -ArgumentList $appPattern + } + default { + # Target is a specific username - remove app for that user only + Invoke-NonBlocking -ScriptBlock { + param($pattern, $user) + $userAccount = New-Object System.Security.Principal.NTAccount($user) + $userSid = $userAccount.Translate([System.Security.Principal.SecurityIdentifier]).Value + Get-AppxPackage -Name $pattern -User $userSid | Remove-AppxPackage -User $userSid -ErrorAction Continue + } -ArgumentList @($appPattern, $targetUser) + } + } + } + catch { + if ($DebugPreference -ne "SilentlyContinue") { + Write-Host "Something went wrong while trying to remove $app" -ForegroundColor Yellow + Write-Host $psitem.Exception.StackTrace -ForegroundColor Gray + } + } + } + + Write-Host "" +} \ No newline at end of file diff --git a/Scripts/CLI/AwaitKeyToExit.ps1 b/Scripts/CLI/AwaitKeyToExit.ps1 new file mode 100644 index 0000000..8ba892c --- /dev/null +++ b/Scripts/CLI/AwaitKeyToExit.ps1 @@ -0,0 +1,11 @@ +function AwaitKeyToExit { + # Suppress prompt if Silent parameter was passed + if (-not $Silent) { + Write-Output "" + Write-Output "Press any key to exit..." + $null = [System.Console]::ReadKey() + } + + Stop-Transcript + Exit +} \ No newline at end of file diff --git a/Scripts/Features/CreateSystemRestorePoint.ps1 b/Scripts/Features/CreateSystemRestorePoint.ps1 new file mode 100644 index 0000000..bbef9cb --- /dev/null +++ b/Scripts/Features/CreateSystemRestorePoint.ps1 @@ -0,0 +1,104 @@ +function CreateSystemRestorePoint { + $SysRestore = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore" -Name "RPSessionInterval" + $failed = $false + + if ($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') { + $enableSystemRestoreJob = Start-Job { + try { + Enable-ComputerRestore -Drive "$env:SystemDrive" + } + catch { + return "Error: Failed to enable System Restore: $_" + } + return $null + } + + $enableSystemRestoreJobDone = $enableSystemRestoreJob | Wait-Job -TimeOut 20 + + if (-not $enableSystemRestoreJobDone) { + Remove-Job -Job $enableSystemRestoreJob -Force -ErrorAction SilentlyContinue + Write-Host "Error: Failed to enable system restore and create restore point, operation timed out" -ForegroundColor Red + $failed = $true + } + else { + $result = Receive-Job $enableSystemRestoreJob + Remove-Job -Job $enableSystemRestoreJob -ErrorAction SilentlyContinue + if ($result) { + Write-Host $result -ForegroundColor Red + $failed = $true + } + } + } + else { + Write-Host "" + $failed = $true + } + } + + if (-not $failed) { + $createRestorePointJob = Start-Job { + # Find existing restore points that are less than 24 hours old + try { + $recentRestorePoints = Get-ComputerRestorePoint | Where-Object { (Get-Date) - [System.Management.ManagementDateTimeConverter]::ToDateTime($_.CreationTime) -le (New-TimeSpan -Hours 24) } + } + catch { + return @{ Success = $false; Message = "Error: Unable to retrieve existing restore points: $_" } + } + + if ($recentRestorePoints.Count -eq 0) { + try { + Checkpoint-Computer -Description "Restore point created by Win11Debloat" -RestorePointType "MODIFY_SETTINGS" + return @{ Success = $true; Message = "System restore point created successfully" } + } + catch { + return @{ Success = $false; Message = "Error: Unable to create restore point: $_" } + } + } + else { + return @{ Success = $true; Message = "A recent restore point already exists, no new restore point was created" } + } + } + + $createRestorePointJobDone = $createRestorePointJob | Wait-Job -TimeOut 20 + + if (-not $createRestorePointJobDone) { + Remove-Job -Job $createRestorePointJob -Force -ErrorAction SilentlyContinue + Write-Host "Error: Failed to create system restore point, operation timed out" -ForegroundColor Red + $failed = $true + } + else { + $result = Receive-Job $createRestorePointJob + Remove-Job -Job $createRestorePointJob -ErrorAction SilentlyContinue + if ($result.Success) { + Write-Host $result.Message + } + else { + Write-Host $result.Message -ForegroundColor Red + $failed = $true + } + } + } + + # Ensure that the user is aware if creating a restore point failed, and give them the option to continue without a restore point or cancel the script + if ($failed) { + if ($script:GuiWindow) { + $result = Show-MessageBox "Failed to create a system restore point. Do you want to continue without a restore point?" "Restore Point Creation Failed" "YesNo" "Warning" + + if ($result -ne "Yes") { + $script:CancelRequested = $true + return + } + } + elseif (-not $Silent) { + 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 + } + } + + Write-Host "Warning: Continuing without restore point" -ForegroundColor Yellow + } +} \ No newline at end of file diff --git a/Scripts/Features/DisableStoreSearchSuggestions.ps1 b/Scripts/Features/DisableStoreSearchSuggestions.ps1 new file mode 100644 index 0000000..be76cb4 --- /dev/null +++ b/Scripts/Features/DisableStoreSearchSuggestions.ps1 @@ -0,0 +1,52 @@ +# Disables Microsoft Store search suggestions in the start menu for all users by denying access to the Store app database file for each user +function DisableStoreSearchSuggestionsForAllUsers { + # Get path to Store app database for all users + $userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages" + $usersStoreDbPaths = get-childitem -path $userPathString + + # Go through all users and disable start search suggestions + ForEach ($storeDbPath in $usersStoreDbPaths) { + DisableStoreSearchSuggestions ($storeDbPath.FullName + "\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db") + } + + # Also disable start search suggestions for the default user profile + $defaultStoreDbPath = GetUserDirectory -userName "Default" -fileName "AppData\Local\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db" -exitIfPathNotFound $false + DisableStoreSearchSuggestions $defaultStoreDbPath +} + + +# Disables Microsoft Store search suggestions in the start menu by denying access to the Store app database file +function DisableStoreSearchSuggestions { + param ( + $StoreAppsDatabase = "$env:LocalAppData\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db" + ) + + # Change path to correct user if a user was specified + if ($script:Params.ContainsKey("User")) { + $StoreAppsDatabase = GetUserDirectory -userName "$(GetUserName)" -fileName "AppData\Local\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState\store.db" -exitIfPathNotFound $false + } + + $userName = [regex]::Match($StoreAppsDatabase, '(?:Users\\)([^\\]+)(?:\\AppData)').Groups[1].Value + + # 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 + + if (-not (Test-Path -Path $storeDbDir)) { + New-Item -Path $storeDbDir -ItemType Directory -Force | Out-Null + } + + New-Item -Path $StoreAppsDatabase -ItemType File -Force | Out-Null + } + + $AccountSid = [System.Security.Principal.SecurityIdentifier]::new('S-1-1-0') # 'EVERYONE' group + $Acl = Get-Acl -Path $StoreAppsDatabase + $Ace = [System.Security.AccessControl.FileSystemAccessRule]::new($AccountSid, 'FullControl', 'Deny') + $Acl.SetAccessRule($Ace) | Out-Null + Set-Acl -Path $StoreAppsDatabase -AclObject $Acl | Out-Null + + Write-Host "Disabled Microsoft Store search suggestions for user $userName" +} \ No newline at end of file diff --git a/Scripts/Features/EnableWindowsFeature.ps1 b/Scripts/Features/EnableWindowsFeature.ps1 new file mode 100644 index 0000000..a8f295b --- /dev/null +++ b/Scripts/Features/EnableWindowsFeature.ps1 @@ -0,0 +1,16 @@ +# Enables a Windows optional feature and pipes its output to the console +function EnableWindowsFeature { + param ( + [string]$FeatureName + ) + + $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() + } +} \ No newline at end of file diff --git a/Scripts/Features/ImportRegistryFile.ps1 b/Scripts/Features/ImportRegistryFile.ps1 new file mode 100644 index 0000000..68d18f4 --- /dev/null +++ b/Scripts/Features/ImportRegistryFile.ps1 @@ -0,0 +1,69 @@ +# Import & execute regfile +function ImportRegistryFile { + param ( + $message, + $path + ) + + Write-Host $message + + # Validate that the regfile exists in both locations + if (-not (Test-Path "$script:RegfilesPath\$path") -or -not (Test-Path "$script:RegfilesPath\Sysprep\$path")) { + Write-Host "Error: Unable to find registry file: $path" -ForegroundColor Red + Write-Host "" + return + } + + # Reset exit code before running reg.exe for reliable success detection + $global:LASTEXITCODE = 0 + + if ($script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User")) { + # 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" + } + + $regResult = Invoke-NonBlocking -ScriptBlock { + param($datPath, $regFilePath) + $global:LASTEXITCODE = 0 + reg load "HKU\Default" $datPath | Out-Null + $output = reg import $regFilePath 2>&1 + $code = $LASTEXITCODE + reg unload "HKU\Default" | Out-Null + return @{ Output = $output; ExitCode = $code } + } -ArgumentList @($hiveDatPath, "$script:RegfilesPath\Sysprep\$path") + } + else { + $regResult = Invoke-NonBlocking -ScriptBlock { + param($regFilePath) + $global:LASTEXITCODE = 0 + $output = reg import $regFilePath 2>&1 + return @{ Output = $output; ExitCode = $LASTEXITCODE } + } -ArgumentList "$script:RegfilesPath\$path" + } + + $regOutput = $regResult.Output + $hasSuccess = $regResult.ExitCode -eq 0 + + 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) { + Write-Host "Failed importing registry file: $path" -ForegroundColor Red + } + + Write-Host "" +} \ No newline at end of file diff --git a/Scripts/Features/ReplaceStartMenu.ps1 b/Scripts/Features/ReplaceStartMenu.ps1 new file mode 100644 index 0000000..306482c --- /dev/null +++ b/Scripts/Features/ReplaceStartMenu.ps1 @@ -0,0 +1,83 @@ +# Replace the startmenu for all users, when using the default startmenuTemplate this clears all pinned apps +# Credit: https://lazyadmin.nl/win-11/customize-windows-11-start-menu-layout/ +function ReplaceStartMenuForAllUsers { + param ( + $startMenuTemplate = "$script:AssetsPath/Start/start2.bin" + ) + + Write-Host "> Removing all pinned apps from the start menu for all users..." + + # 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 + } + + # Get path to start menu file for all users + $userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" + $usersStartMenuPaths = get-childitem -path $userPathString + + # Go through all users and replace the start menu file + ForEach ($startMenuPath in $usersStartMenuPaths) { + ReplaceStartMenu $startMenuTemplate "$($startMenuPath.Fullname)\start2.bin" + } + + # Also replace the start menu file for the default user profile + $defaultStartMenuPath = GetUserDirectory -userName "Default" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" -exitIfPathNotFound $false + + # 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" + } + + # Copy template to default profile + Copy-Item -Path $startMenuTemplate -Destination $defaultStartMenuPath -Force + Write-Host "Replaced start menu for the default user profile" + Write-Host "" +} + + +# Replace the startmenu for all users, when using the default startmenuTemplate this clears all pinned apps +# Credit: https://lazyadmin.nl/win-11/customize-windows-11-start-menu-layout/ +function ReplaceStartMenu { + param ( + $startMenuTemplate = "$script:AssetsPath/Start/start2.bin", + $startMenuBinFile = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" + ) + + # Change path to correct user if a user was specified + if ($script:Params.ContainsKey("User")) { + $startMenuBinFile = GetUserDirectory -userName "$(GetUserName)" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -exitIfPathNotFound $false + } + + # 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 + } + + 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 + } + + $userName = [regex]::Match($startMenuBinFile, '(?:Users\\)([^\\]+)(?:\\AppData)').Groups[1].Value + + $backupBinFile = $startMenuBinFile + ".bak" + + if (Test-Path $startMenuBinFile) { + # Backup current start menu file + Move-Item -Path $startMenuBinFile -Destination $backupBinFile -Force + } + 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 + } + + # Copy template file + Copy-Item -Path $startMenuTemplate -Destination $startMenuBinFile -Force + + Write-Host "Replaced start menu for user $userName" +} \ No newline at end of file diff --git a/Scripts/Features/RestartExplorer.ps1 b/Scripts/Features/RestartExplorer.ps1 new file mode 100644 index 0000000..054f4fa --- /dev/null +++ b/Scripts/Features/RestartExplorer.ps1 @@ -0,0 +1,26 @@ +# Restart the Windows Explorer process +function RestartExplorer { + Write-Host "> Attempting to restart the Windows Explorer process to apply all changes..." + + if ($script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User") -or $script:Params.ContainsKey("NoRestartExplorer")) { + Write-Host "Explorer process restart was skipped, please manually reboot your PC to apply all changes" -ForegroundColor Yellow + return + } + + foreach ($paramKey in $script:Params.Keys) { + if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) { + $feature = $script:Features[$paramKey] + Write-Host "Warning: '$($feature.Action) $($feature.Label)' requires a reboot to take full effect" -ForegroundColor Yellow + } + } + + # Only restart if the powershell process matches the OS architecture. + # Restarting explorer from a 32bit PowerShell window will fail on a 64bit OS + if ([Environment]::Is64BitProcess -eq [Environment]::Is64BitOperatingSystem) { + Write-Host "Restarting the Windows Explorer process... (This may cause your screen to flicker)" + Stop-Process -processName: Explorer -Force + } + else { + Write-Host "Unable to restart Windows Explorer process, please manually reboot your PC to apply all changes" -ForegroundColor Yellow + } +} \ No newline at end of file diff --git a/Scripts/GUI/Show-MainWindow.ps1 b/Scripts/GUI/Show-MainWindow.ps1 index c4d3dcb..b05f194 100644 --- a/Scripts/GUI/Show-MainWindow.ps1 +++ b/Scripts/GUI/Show-MainWindow.ps1 @@ -1419,6 +1419,15 @@ function Show-MainWindow { UpdateNavigationButtons }) + # Add event handler for tab changes + $tabControl.Add_SelectionChanged({ + # Regenerate overview when switching to Overview tab + if ($tabControl.SelectedIndex -eq ($tabControl.Items.Count - 2)) { + GenerateOverview + } + UpdateNavigationButtons + }) + # Handle Load Defaults button $loadDefaultsBtn = $window.FindName('LoadDefaultsBtn') $loadDefaultsBtn.Add_Click({ diff --git a/Scripts/Get.ps1 b/Scripts/Get.ps1 index 9322670..6b135ff 100644 --- a/Scripts/Get.ps1 +++ b/Scripts/Get.ps1 @@ -33,6 +33,7 @@ param ( [switch]$PreventUpdateAutoReboot, [switch]$DisableDeliveryOptimization, [switch]$DisableBing, + [switch]$DisableStoreSearchSuggestions, [switch]$DisableDesktopSpotlight, [switch]$DisableLockscreenTips, [switch]$DisableSuggestions, diff --git a/Win11Debloat.ps1 b/Win11Debloat.ps1 old mode 100644 new mode 100755 index a34a9b5..be70676 --- a/Win11Debloat.ps1 +++ b/Win11Debloat.ps1 @@ -35,6 +35,7 @@ param ( [switch]$PreventUpdateAutoReboot, [switch]$DisableDeliveryOptimization, [switch]$DisableBing, + [switch]$DisableStoreSearchSuggestions, [switch]$DisableSearchHighlights, [switch]$DisableDesktopSpotlight, [switch]$DisableLockscreenTips, @@ -217,7 +218,12 @@ if (-not $script:WingetInstalled -and -not $Silent) { # # ################################################################################################################## +# Load app removal functions +. "$PSScriptRoot/Scripts/AppRemoval/ForceRemoveEdge.ps1" +. "$PSScriptRoot/Scripts/AppRemoval/RemoveApps.ps1" + # Load CLI functions +. "$PSScriptRoot/Scripts/CLI/AwaitKeyToExit.ps1" . "$PSScriptRoot/Scripts/CLI/ShowCLILastUsedSettings.ps1" . "$PSScriptRoot/Scripts/CLI/ShowCLIDefaultModeAppRemovalOptions.ps1" . "$PSScriptRoot/Scripts/CLI/ShowCLIDefaultModeOptions.ps1" @@ -226,6 +232,14 @@ if (-not $script:WingetInstalled -and -not $Silent) { . "$PSScriptRoot/Scripts/CLI/PrintPendingChanges.ps1" . "$PSScriptRoot/Scripts/CLI/PrintHeader.ps1" +# Load Feature functions +. "$PSScriptRoot/Scripts/Features/CreateSystemRestorePoint.ps1" +. "$PSScriptRoot/Scripts/Features/DisableStoreSearchSuggestions.ps1" +. "$PSScriptRoot/Scripts/Features/EnableWindowsFeature.ps1" +. "$PSScriptRoot/Scripts/Features/ImportRegistryFile.ps1" +. "$PSScriptRoot/Scripts/Features/ReplaceStartMenu.ps1" +. "$PSScriptRoot/Scripts/Features/RestartExplorer.ps1" + # Load GUI functions . "$PSScriptRoot/Scripts/GUI/GetSystemUsesDarkMode.ps1" . "$PSScriptRoot/Scripts/GUI/SetWindowThemeResources.ps1" @@ -480,334 +494,6 @@ function CheckModernStandbySupport { } -# Removes apps specified during function call based on the target scope. -function RemoveApps { - param ( - $appslist - ) - - # Determine target from script-level params, defaulting to AllUsers - $targetUser = GetTargetUserForAppRemoval - - $appIndex = 0 - $appCount = @($appsList).Count - - Foreach ($app in $appsList) { - if ($script:CancelRequested) { - return - } - - $appIndex++ - - # Update step name and sub-progress to show which app is being removed (only for bulk removal) - if ($script:ApplySubStepCallback -and $appCount -gt 1) { - & $script:ApplySubStepCallback "Removing apps ($appIndex/$appCount)" $appIndex $appCount - } - - Write-Host "Attempting to remove $app..." - - # Use WinGet only to remove OneDrive and Edge - if (($app -eq "Microsoft.OneDrive") -or ($app -eq "Microsoft.Edge")) { - if ($script:WingetInstalled -eq $false) { - Write-Host "WinGet is either not installed or is outdated, $app could not be removed" -ForegroundColor Red - continue - } - - $appName = $app -replace '\.', '_' - - # Uninstall app via WinGet, or create a scheduled task to uninstall it later - if ($script:Params.ContainsKey("User")) { - RegImport "Adding scheduled task to uninstall $app for user $(GetUserName)..." "Uninstall_$($appName).reg" - } - elseif ($script:Params.ContainsKey("Sysprep")) { - RegImport "Adding scheduled task to uninstall $app after for new users..." "Uninstall_$($appName).reg" - } - else { - # Uninstall app via WinGet - $wingetOutput = Invoke-NonBlocking -ScriptBlock { - param($appId) - winget uninstall --accept-source-agreements --disable-interactivity --id $appId - } -ArgumentList $app - - If (($app -eq "Microsoft.Edge") -and (Select-String -InputObject $wingetOutput -Pattern "Uninstall failed with exit code")) { - Write-Host "Unable to uninstall Microsoft Edge via WinGet" -ForegroundColor Red - - if ($script:GuiWindow) { - $result = Show-MessageBox -Message 'Unable to uninstall Microsoft Edge via WinGet. Would you like to forcefully uninstall it? NOT RECOMMENDED!' -Title 'Force Uninstall Microsoft Edge?' -Button 'YesNo' -Icon 'Warning' - - if ($result -eq 'Yes') { - Write-Host "" - ForceRemoveEdge - } - } - elseif ($( Read-Host -Prompt "Would you like to forcefully uninstall Microsoft Edge? NOT RECOMMENDED! (y/n)" ) -eq 'y') { - Write-Host "" - ForceRemoveEdge - } - } - } - - continue - } - - # Use Remove-AppxPackage to remove all other apps - $appPattern = '*' + $app + '*' - - try { - switch ($targetUser) { - "AllUsers" { - # Remove installed app for all existing users, and from OS image - Invoke-NonBlocking -ScriptBlock { - param($pattern) - Get-AppxPackage -Name $pattern -AllUsers | Remove-AppxPackage -AllUsers -ErrorAction Continue - Get-AppxProvisionedPackage -Online | Where-Object { $_.PackageName -like $pattern } | ForEach-Object { Remove-ProvisionedAppxPackage -Online -AllUsers -PackageName $_.PackageName } - } -ArgumentList $appPattern - } - "CurrentUser" { - # Remove installed app for current user only - Invoke-NonBlocking -ScriptBlock { - param($pattern) - Get-AppxPackage -Name $pattern | Remove-AppxPackage -ErrorAction Continue - } -ArgumentList $appPattern - } - default { - # Target is a specific username - remove app for that user only - Invoke-NonBlocking -ScriptBlock { - param($pattern, $user) - $userAccount = New-Object System.Security.Principal.NTAccount($user) - $userSid = $userAccount.Translate([System.Security.Principal.SecurityIdentifier]).Value - Get-AppxPackage -Name $pattern -User $userSid | Remove-AppxPackage -User $userSid -ErrorAction Continue - } -ArgumentList @($appPattern, $targetUser) - } - } - } - catch { - if ($DebugPreference -ne "SilentlyContinue") { - Write-Host "Something went wrong while trying to remove $app" -ForegroundColor Yellow - Write-Host $psitem.Exception.StackTrace -ForegroundColor Gray - } - } - } - - Write-Host "" -} - - -# Forcefully removes Microsoft Edge using its uninstaller -# Credit: Based on work from loadstring1 & ave9858 -function ForceRemoveEdge { - Write-Host "> Forcefully uninstalling Microsoft Edge..." - - $regView = [Microsoft.Win32.RegistryView]::Registry32 - $hklm = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $regView) - $hklm.CreateSubKey('SOFTWARE\Microsoft\EdgeUpdateDev').SetValue('AllowUninstall', '') - - # Create stub (This somehow allows uninstalling Edge) - $edgeStub = "$env:SystemRoot\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe" - New-Item $edgeStub -ItemType Directory | Out-Null - New-Item "$edgeStub\MicrosoftEdge.exe" | Out-Null - - # Remove edge - $uninstallRegKey = $hklm.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge') - if ($null -ne $uninstallRegKey) { - Write-Host "Running uninstaller..." - $uninstallString = $uninstallRegKey.GetValue('UninstallString') + ' --force-uninstall' - Invoke-NonBlocking -ScriptBlock { - param($cmd) - Start-Process cmd.exe "/c $cmd" -WindowStyle Hidden -Wait - } -ArgumentList $uninstallString - - Write-Host "Removing leftover files..." - - $edgePaths = @( - "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft Edge.lnk", - "$env:APPDATA\Microsoft\Internet Explorer\Quick Launch\Microsoft Edge.lnk", - "$env:APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Microsoft Edge.lnk", - "$env:APPDATA\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar\Tombstones\Microsoft Edge.lnk", - "$env:PUBLIC\Desktop\Microsoft Edge.lnk", - "$env:USERPROFILE\Desktop\Microsoft Edge.lnk", - "$edgeStub" - ) - - foreach ($path in $edgePaths) { - if (Test-Path -Path $path) { - Remove-Item -Path $path -Force -Recurse -ErrorAction SilentlyContinue - Write-Host " Removed $path" -ForegroundColor DarkGray - } - } - - Write-Host "Cleaning up registry..." - - # Remove MS Edge from autostart - reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v "MicrosoftEdgeAutoLaunch_A9F6DCE4ABADF4F51CF45CD7129E3C6C" /f *>$null - reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v "Microsoft Edge Update" /f *>$null - reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run" /v "MicrosoftEdgeAutoLaunch_A9F6DCE4ABADF4F51CF45CD7129E3C6C" /f *>$null - reg delete "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\StartupApproved\Run" /v "Microsoft Edge Update" /f *>$null - - Write-Host "Microsoft Edge was uninstalled" - } - else { - Write-Host "Unable to forcefully uninstall Microsoft Edge, uninstaller could not be found" -ForegroundColor Red - } -} - - -# Import & execute regfile -function RegImport { - param ( - $message, - $path - ) - - Write-Host $message - - # Validate that the regfile exists in both locations - if (-not (Test-Path "$script:RegfilesPath\$path") -or -not (Test-Path "$script:RegfilesPath\Sysprep\$path")) { - Write-Host "Error: Unable to find registry file: $path" -ForegroundColor Red - Write-Host "" - return - } - - # Reset exit code before running reg.exe for reliable success detection - $global:LASTEXITCODE = 0 - - if ($script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User")) { - # 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" - } - - $regResult = Invoke-NonBlocking -ScriptBlock { - param($datPath, $regFilePath) - $global:LASTEXITCODE = 0 - reg load "HKU\Default" $datPath | Out-Null - $output = reg import $regFilePath 2>&1 - $code = $LASTEXITCODE - reg unload "HKU\Default" | Out-Null - return @{ Output = $output; ExitCode = $code } - } -ArgumentList @($hiveDatPath, "$script:RegfilesPath\Sysprep\$path") - } - else { - $regResult = Invoke-NonBlocking -ScriptBlock { - param($regFilePath) - $global:LASTEXITCODE = 0 - $output = reg import $regFilePath 2>&1 - return @{ Output = $output; ExitCode = $LASTEXITCODE } - } -ArgumentList "$script:RegfilesPath\$path" - } - - $regOutput = $regResult.Output - $hasSuccess = $regResult.ExitCode -eq 0 - - 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) { - Write-Host "Failed importing registry file: $path" -ForegroundColor Red - } - - Write-Host "" -} - - -# Replace the startmenu for all users, when using the default startmenuTemplate this clears all pinned apps -# Credit: https://lazyadmin.nl/win-11/customize-windows-11-start-menu-layout/ -function ReplaceStartMenuForAllUsers { - param ( - $startMenuTemplate = "$script:AssetsPath/Start/start2.bin" - ) - - Write-Host "> Removing all pinned apps from the start menu for all users..." - - # 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 - } - - # Get path to start menu file for all users - $userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" - $usersStartMenuPaths = get-childitem -path $userPathString - - # Go through all users and replace the start menu file - ForEach ($startMenuPath in $usersStartMenuPaths) { - ReplaceStartMenu $startMenuTemplate "$($startMenuPath.Fullname)\start2.bin" - } - - # Also replace the start menu file for the default user profile - $defaultStartMenuPath = GetUserDirectory -userName "Default" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" -exitIfPathNotFound $false - - # 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" - } - - # Copy template to default profile - Copy-Item -Path $startMenuTemplate -Destination $defaultStartMenuPath -Force - Write-Host "Replaced start menu for the default user profile" - Write-Host "" -} - - -# Replace the startmenu for all users, when using the default startmenuTemplate this clears all pinned apps -# Credit: https://lazyadmin.nl/win-11/customize-windows-11-start-menu-layout/ -function ReplaceStartMenu { - param ( - $startMenuTemplate = "$script:AssetsPath/Start/start2.bin", - $startMenuBinFile = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" - ) - - # Change path to correct user if a user was specified - if ($script:Params.ContainsKey("User")) { - $startMenuBinFile = GetUserDirectory -userName "$(GetUserName)" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -exitIfPathNotFound $false - } - - # 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 - } - - 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 - } - - $userName = [regex]::Match($startMenuBinFile, '(?:Users\\)([^\\]+)(?:\\AppData)').Groups[1].Value - - $backupBinFile = $startMenuBinFile + ".bak" - - if (Test-Path $startMenuBinFile) { - # Backup current start menu file - Move-Item -Path $startMenuBinFile -Destination $backupBinFile -Force - } - 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 - } - - # Copy template file - Copy-Item -Path $startMenuTemplate -Destination $startMenuBinFile -Force - - Write-Host "Replaced start menu for user $userName" -} - - # Generates a list of apps to remove based on the Apps parameter function GenerateAppsList { if (-not ($script:Params["Apps"] -and $script:Params["Apps"] -is [string])) { @@ -843,11 +529,11 @@ function ExecuteParameter { $feature = $script:Features[$paramKey] } - # If feature has RegistryKey and ApplyText, use dynamic RegImport + # If feature has RegistryKey and ApplyText, use dynamic ImportRegistryFile if ($feature -and $feature.RegistryKey -and $feature.ApplyText) { - RegImport "> $($feature.ApplyText)" $feature.RegistryKey + ImportRegistryFile "> $($feature.ApplyText)" $feature.RegistryKey - # Handle special cases that have additional logic after RegImport + # Handle special cases that have additional logic after ImportRegistryFile switch ($paramKey) { 'DisableBing' { # Also remove the app package for Bing search @@ -950,6 +636,19 @@ function ExecuteParameter { ReplaceStartMenuForAllUsers $script:Params.Item("ReplaceStartAllUsers") return } + 'DisableStoreSearchSuggestions' { + if ($script:Params.ContainsKey("Sysprep")) { + Write-Host "> Disabling Microsoft Store search suggestions in the start menu for all users..." + DisableStoreSearchSuggestionsForAllUsers + Write-Host "" + return + } + + Write-Host "> Disabling Microsoft Store search suggestions for user $(GetUserName)..." + DisableStoreSearchSuggestions + Write-Host "" + return + } } } @@ -1015,171 +714,6 @@ function ExecuteAllChanges { } -function CreateSystemRestorePoint { - $SysRestore = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore" -Name "RPSessionInterval" - $failed = $false - - if ($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') { - $enableSystemRestoreJob = Start-Job { - try { - Enable-ComputerRestore -Drive "$env:SystemDrive" - } - catch { - return "Error: Failed to enable System Restore: $_" - } - return $null - } - - $enableSystemRestoreJobDone = $enableSystemRestoreJob | Wait-Job -TimeOut 20 - - if (-not $enableSystemRestoreJobDone) { - Remove-Job -Job $enableSystemRestoreJob -Force -ErrorAction SilentlyContinue - Write-Host "Error: Failed to enable system restore and create restore point, operation timed out" -ForegroundColor Red - $failed = $true - } - else { - $result = Receive-Job $enableSystemRestoreJob - Remove-Job -Job $enableSystemRestoreJob -ErrorAction SilentlyContinue - if ($result) { - Write-Host $result -ForegroundColor Red - $failed = $true - } - } - } - else { - Write-Host "" - $failed = $true - } - } - - if (-not $failed) { - $createRestorePointJob = Start-Job { - # Find existing restore points that are less than 24 hours old - try { - $recentRestorePoints = Get-ComputerRestorePoint | Where-Object { (Get-Date) - [System.Management.ManagementDateTimeConverter]::ToDateTime($_.CreationTime) -le (New-TimeSpan -Hours 24) } - } - catch { - return @{ Success = $false; Message = "Error: Unable to retrieve existing restore points: $_" } - } - - if ($recentRestorePoints.Count -eq 0) { - try { - Checkpoint-Computer -Description "Restore point created by Win11Debloat" -RestorePointType "MODIFY_SETTINGS" - return @{ Success = $true; Message = "System restore point created successfully" } - } - catch { - return @{ Success = $false; Message = "Error: Unable to create restore point: $_" } - } - } - else { - return @{ Success = $true; Message = "A recent restore point already exists, no new restore point was created" } - } - } - - $createRestorePointJobDone = $createRestorePointJob | Wait-Job -TimeOut 20 - - if (-not $createRestorePointJobDone) { - Remove-Job -Job $createRestorePointJob -Force -ErrorAction SilentlyContinue - Write-Host "Error: Failed to create system restore point, operation timed out" -ForegroundColor Red - $failed = $true - } - else { - $result = Receive-Job $createRestorePointJob - Remove-Job -Job $createRestorePointJob -ErrorAction SilentlyContinue - if ($result.Success) { - Write-Host $result.Message - } - else { - Write-Host $result.Message -ForegroundColor Red - $failed = $true - } - } - } - - # Ensure that the user is aware if creating a restore point failed, and give them the option to continue without a restore point or cancel the script - if ($failed) { - if ($script:GuiWindow) { - $result = Show-MessageBox "Failed to create a system restore point. Do you want to continue without a restore point?" "Restore Point Creation Failed" "YesNo" "Warning" - - if ($result -ne "Yes") { - $script:CancelRequested = $true - return - } - } - elseif (-not $Silent) { - 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 - } - } - - Write-Host "Warning: Continuing without restore point" -ForegroundColor Yellow - } -} - - -# Enables a Windows optional feature and pipes its output to the console -function EnableWindowsFeature { - param ( - [string]$FeatureName - ) - - $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() - } -} - - -# Restart the Windows Explorer process -function RestartExplorer { - Write-Host "> Attempting to restart the Windows Explorer process to apply all changes..." - - if ($script:Params.ContainsKey("Sysprep") -or $script:Params.ContainsKey("User") -or $script:Params.ContainsKey("NoRestartExplorer")) { - Write-Host "Explorer process restart was skipped, please manually reboot your PC to apply all changes" -ForegroundColor Yellow - return - } - - foreach ($paramKey in $script:Params.Keys) { - if ($script:Features.ContainsKey($paramKey) -and $script:Features[$paramKey].RequiresReboot -eq $true) { - $feature = $script:Features[$paramKey] - Write-Host "Warning: '$($feature.Action) $($feature.Label)' requires a reboot to take full effect" -ForegroundColor Yellow - } - } - - # Only restart if the powershell process matches the OS architecture. - # Restarting explorer from a 32bit PowerShell window will fail on a 64bit OS - if ([Environment]::Is64BitProcess -eq [Environment]::Is64BitOperatingSystem) { - Write-Host "Restarting the Windows Explorer process... (This may cause your screen to flicker)" - Stop-Process -processName: Explorer -Force - } - else { - Write-Host "Unable to restart Windows Explorer process, please manually reboot your PC to apply all changes" -ForegroundColor Yellow - } -} - - -function AwaitKeyToExit { - # Suppress prompt if Silent parameter was passed - if (-not $Silent) { - Write-Output "" - Write-Output "Press any key to exit..." - $null = [System.Console]::ReadKey() - } - - Stop-Transcript - Exit -} - - ################################################################################################################## # #