mirror of
https://github.com/Raphire/Win11Debloat.git
synced 2026-04-03 05:56:25 +00:00
Add option to disable MS Store app results in Windows Search (#509)
This also includes: * Update README for DisableSearchHighlights and DisableFindMyDevice * Extracted more logic from main script into separate scripts * Add event handler for tab changes to regenerate overview
This commit is contained in:
@@ -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
|
||||
},
|
||||
{
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
"Name": "DisableBing",
|
||||
"Value": true
|
||||
},
|
||||
{
|
||||
"Name": "DisableStoreSearchSuggestions",
|
||||
"Value": true
|
||||
},
|
||||
{
|
||||
"Name": "DisableSearchHighlights",
|
||||
"Value": true
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
57
Scripts/AppRemoval/ForceRemoveEdge.ps1
Normal file
57
Scripts/AppRemoval/ForceRemoveEdge.ps1
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
111
Scripts/AppRemoval/RemoveApps.ps1
Normal file
111
Scripts/AppRemoval/RemoveApps.ps1
Normal file
@@ -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 ""
|
||||
}
|
||||
11
Scripts/CLI/AwaitKeyToExit.ps1
Normal file
11
Scripts/CLI/AwaitKeyToExit.ps1
Normal file
@@ -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
|
||||
}
|
||||
104
Scripts/Features/CreateSystemRestorePoint.ps1
Normal file
104
Scripts/Features/CreateSystemRestorePoint.ps1
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
52
Scripts/Features/DisableStoreSearchSuggestions.ps1
Normal file
52
Scripts/Features/DisableStoreSearchSuggestions.ps1
Normal file
@@ -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"
|
||||
}
|
||||
16
Scripts/Features/EnableWindowsFeature.ps1
Normal file
16
Scripts/Features/EnableWindowsFeature.ps1
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
69
Scripts/Features/ImportRegistryFile.ps1
Normal file
69
Scripts/Features/ImportRegistryFile.ps1
Normal file
@@ -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 ""
|
||||
}
|
||||
83
Scripts/Features/ReplaceStartMenu.ps1
Normal file
83
Scripts/Features/ReplaceStartMenu.ps1
Normal file
@@ -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"
|
||||
}
|
||||
26
Scripts/Features/RestartExplorer.ps1
Normal file
26
Scripts/Features/RestartExplorer.ps1
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -33,6 +33,7 @@ param (
|
||||
[switch]$PreventUpdateAutoReboot,
|
||||
[switch]$DisableDeliveryOptimization,
|
||||
[switch]$DisableBing,
|
||||
[switch]$DisableStoreSearchSuggestions,
|
||||
[switch]$DisableDesktopSpotlight,
|
||||
[switch]$DisableLockscreenTips,
|
||||
[switch]$DisableSuggestions,
|
||||
|
||||
526
Win11Debloat.ps1
Normal file → Executable file
526
Win11Debloat.ps1
Normal file → Executable file
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
##################################################################################################################
|
||||
# #
|
||||
|
||||
Reference in New Issue
Block a user