Update start menu backup/restore with timestamped filenames (#672)

This commit is contained in:
Jeffrey
2026-06-24 17:32:45 +02:00
committed by GitHub
parent 693b805114
commit 95b583606d
2 changed files with 102 additions and 50 deletions

View File

@@ -113,11 +113,15 @@ function ReplaceStartMenu {
return return
} }
$backupBinFile = $startMenuBinFile + ".bak" $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$backupFileName = "Win11Debloat-StartBackup-$timestamp.bak"
$startMenuDir = Split-Path $startMenuBinFile -Parent
$backupBinFile = Join-Path $startMenuDir $backupFileName
if (Test-Path $startMenuBinFile) { if (Test-Path $startMenuBinFile) {
# Backup current start menu file # Backup current start menu file
Move-Item -Path $startMenuBinFile -Destination $backupBinFile -Force Copy-Item -Path $startMenuBinFile -Destination $backupBinFile -Force
Write-Verbose "Start menu backup for user $userName saved to $backupFileName"
} }
else { else {
Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow Write-Host "Unable to find original start2.bin file for user $userName, no backup was created for this user" -ForegroundColor Yellow
@@ -189,6 +193,55 @@ function GetStartMenuUserNameFromPath {
} }
<#
.SYNOPSIS
Returns the path to the latest start menu backup file for the given scope.
.DESCRIPTION
Resolves the LocalState folder for the specified scope and returns the
full path to the most recent Win11Debloat-StartBackup-*.bak file, or
$null if no backup exists.
For CurrentUser, uses $env:LOCALAPPDATA directly. For AllUsers, scans
every user profile.
.PARAMETER Scope
The scope to check: CurrentUser or AllUsers.
.EXAMPLE
$backupPath = Get-StartMenuBackupPath -Scope 'CurrentUser'
.EXAMPLE
$backupPath = Get-StartMenuBackupPath -Scope 'AllUsers'
#>
function Get-StartMenuBackupPath {
param(
[Parameter(Mandatory)]
[ValidateSet('CurrentUser', 'AllUsers')]
[string]$Scope
)
if ($Scope -eq 'CurrentUser') {
$localStateDir = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
$latestBackup = Get-ChildItem -Path (Join-Path $localStateDir 'Win11Debloat-StartBackup-*.bak') -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
Select-Object -First 1
if ($latestBackup) { return $latestBackup.FullName }
return $null
}
else {
$userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
$usersStartMenuPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue
foreach ($startMenuPath in $usersStartMenuPaths) {
$latestBackup = Get-ChildItem -Path (Join-Path $startMenuPath.FullName 'Win11Debloat-StartBackup-*.bak') -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
Select-Object -First 1
if ($latestBackup) { return $latestBackup.FullName }
}
return $null
}
}
<# <#
.SYNOPSIS .SYNOPSIS
@@ -204,14 +257,14 @@ function GetStartMenuUserNameFromPath {
The full path to the user's start2.bin file to restore. The full path to the user's start2.bin file to restore.
.PARAMETER BackupFilePath .PARAMETER BackupFilePath
Path to the backup file to restore from. If omitted, defaults to Path to the backup file to restore from. If omitted, automatically
StartMenuBinFile with a .bak extension. finds the latest Win11Debloat-StartBackup-*.bak file.
.EXAMPLE .EXAMPLE
RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin"
.EXAMPLE .EXAMPLE
RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -BackupFilePath "C:\Backups\start2.bin" RestoreStartMenuFromBackup -StartMenuBinFile "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin" -BackupFilePath "C:\Backups\Win11Debloat-StartBackup-20260101_120000.bak"
#> #>
function RestoreStartMenuFromBackup { function RestoreStartMenuFromBackup {
param( param(
@@ -222,12 +275,28 @@ function RestoreStartMenuFromBackup {
$userName = GetStartMenuUserNameFromPath -StartMenuBinFile $StartMenuBinFile $userName = GetStartMenuUserNameFromPath -StartMenuBinFile $StartMenuBinFile
$backupBinFile = if ([string]::IsNullOrWhiteSpace($BackupFilePath)) { $backupBinFile = if ([string]::IsNullOrWhiteSpace($BackupFilePath)) {
$StartMenuBinFile + '.bak' # Auto-detect latest backup in the same folder as the start2.bin
$startMenuDir = Split-Path $StartMenuBinFile -Parent
$latestBackup = Get-ChildItem -Path (Join-Path $startMenuDir 'Win11Debloat-StartBackup-*.bak') -ErrorAction SilentlyContinue |
Sort-Object Name -Descending |
Select-Object -First 1
if ($latestBackup) { $latestBackup.FullName } else { $null }
} }
else { else {
$BackupFilePath $BackupFilePath
} }
$currentBinBackup = $StartMenuBinFile + '.restore.bak' $restoreTimestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$restoreBackupFileName = "Win11Debloat-StartRestore-$restoreTimestamp.bak"
$currentBinBackup = Join-Path (Split-Path $StartMenuBinFile -Parent) $restoreBackupFileName
if ([string]::IsNullOrWhiteSpace($backupBinFile)) {
return [PSCustomObject]@{
UserName = $userName
Result = $false
Message = "No start menu backup file found for user $userName."
}
}
if ($script:Params.ContainsKey("WhatIf")) { if ($script:Params.ContainsKey("WhatIf")) {
Write-Host "[WhatIf] Restore start menu for user $userName from backup $backupBinFile" -ForegroundColor Cyan Write-Host "[WhatIf] Restore start menu for user $userName from backup $backupBinFile" -ForegroundColor Cyan
@@ -272,37 +341,26 @@ function RestoreStartMenuFromBackup {
Restores the start menu for the current target user from a backup. Restores the start menu for the current target user from a backup.
.DESCRIPTION .DESCRIPTION
Resolves the start2.bin path for the current user (or the user specified Resolves the start2.bin path for the currently logged-in user, then
via the -User parameter), then delegates to RestoreStartMenuFromBackup. delegates to RestoreStartMenuFromBackup.
Returns early with a warning if the user's start menu path cannot
be resolved.
.PARAMETER BackupFilePath .PARAMETER BackupFilePath
Path to the backup file to restore from. If omitted, defaults to Path to the backup file to restore from. If omitted, automatically
the .bak file alongside the current start2.bin. finds the latest Win11Debloat-StartBackup-*.bak file.
.EXAMPLE .EXAMPLE
RestoreStartMenu RestoreStartMenu
.EXAMPLE .EXAMPLE
RestoreStartMenu -BackupFilePath "C:\Backups\start2.bin" RestoreStartMenu -BackupFilePath "C:\Backups\Win11Debloat-StartBackup-20260101_120000.bak"
#> #>
function RestoreStartMenu { function RestoreStartMenu {
param( param(
[string]$BackupFilePath [string]$BackupFilePath
) )
$targetUserName = GetUserName $targetUserName = $env:USERNAME
$startMenuBinFile = GetStartMenuBinPathForUser -UserName $targetUserName $startMenuBinFile = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin"
if ([string]::IsNullOrWhiteSpace($startMenuBinFile)) {
Write-Host "Unable to resolve start menu path for user $targetUserName, nothing to restore" -ForegroundColor Yellow
return [PSCustomObject]@{
UserName = $targetUserName
Result = $false
Message = "Could not resolve start menu path for user $targetUserName."
}
}
Write-Host "Restoring start menu for user $targetUserName from backup..." Write-Host "Restoring start menu for user $targetUserName from backup..."
@@ -315,19 +373,21 @@ function RestoreStartMenu {
.DESCRIPTION .DESCRIPTION
Iterates over every existing user profile and restores each user's Iterates over every existing user profile and restores each user's
start2.bin from its .bak backup. For the Default user profile, removes start2.bin from the latest backup in their LocalState folder. For the
the start2.bin file (which was previously copied from a template) so Default user profile, removes the start2.bin file (which was previously
that new profiles revert to the system default start menu. copied from a template) so that new profiles revert to the system
default start menu.
.PARAMETER BackupFilePath .PARAMETER BackupFilePath
Path to the backup file to restore from. If omitted, defaults to Path to the backup file to restore from. If omitted, automatically
the .bak file alongside each user's current start2.bin. finds the latest Win11Debloat-StartBackup-*.bak in each user's
LocalState folder.
.EXAMPLE .EXAMPLE
RestoreStartMenuForAllUsers RestoreStartMenuForAllUsers
.EXAMPLE .EXAMPLE
RestoreStartMenuForAllUsers -BackupFilePath "C:\Backups\start2.bin" RestoreStartMenuForAllUsers -BackupFilePath "C:\Backups\Win11Debloat-StartBackup-20260101_120000.bak"
#> #>
function RestoreStartMenuForAllUsers { function RestoreStartMenuForAllUsers {
param( param(

View File

@@ -196,6 +196,10 @@ function Show-RestoreBackupDialog {
$primaryActionBtn.Visibility = 'Visible' $primaryActionBtn.Visibility = 'Visible'
$primaryActionBtn.IsDefault = $true $primaryActionBtn.IsDefault = $true
$chooseRegistryBtn.IsDefault = $false $chooseRegistryBtn.IsDefault = $false
# Show intro panel so user can configure scope & auto-detect
$startMenuAutoBackupCheck.IsChecked = $true
$state.SelectedStartMenuBackupFilePath = $null
& $refreshStartMenuUi & $refreshStartMenuUi
} }
@@ -323,27 +327,14 @@ function Show-RestoreBackupDialog {
} }
if (-not $useManualBackupFile) { if (-not $useManualBackupFile) {
$autoBackupExists = $false $scopeInfo = & $getStartMenuScopeInfo
if ($scope -eq 'AllUsers') { $autoBackupPath = Get-StartMenuBackupPath -Scope $scopeInfo.Scope
$userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" if ($null -eq $autoBackupPath) {
$usersStartMenuPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue $scopeText = $scopeInfo.SummaryText
foreach ($startMenuPath in $usersStartMenuPaths) { Show-MessageBox -Owner $window -Title 'No Backup Found' -Message "No Start Menu backup file was found for $scopeText. Uncheck 'Automatically find Start Menu backup' to select a backup file manually." -Button 'OK' -Icon 'Warning' | Out-Null
if (Test-Path -LiteralPath (Join-Path $startMenuPath.FullName 'start2.bin.bak')) {
$autoBackupExists = $true
break
}
}
}
else {
$autoBackupPath = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState\start2.bin.bak"
$autoBackupExists = Test-Path -LiteralPath $autoBackupPath
}
if (-not $autoBackupExists) {
$scopeText = (& $getStartMenuScopeInfo).SummaryText
Show-MessageBox -Owner $window -Title 'No Backup Found' -Message "No Start Menu backup file was found. You can uncheck the 'Automatically find Start Menu backup' option to select a backup file manually." -Button 'OK' -Icon 'Warning' | Out-Null
return return
} }
$state.SelectedStartMenuBackupFilePath = if ($scopeInfo.Scope -eq 'CurrentUser') { $autoBackupPath } else { $null }
} }
$window.Tag = @{ $window.Tag = @{
@@ -377,6 +368,7 @@ function Show-RestoreBackupDialog {
}) })
$startMenuScopeCombo.Add_SelectionChanged({ $startMenuScopeCombo.Add_SelectionChanged({
$state.SelectedStartMenuBackupFilePath = $null
& $refreshStartMenuUi & $refreshStartMenuUi
}) })