Store user data (backups, logs, settings) under LocalAppData, update start menu backup creation

This commit is contained in:
Jeffrey
2026-06-23 01:04:29 +02:00
parent 5ebc50d36a
commit 88b5f1b629
8 changed files with 46 additions and 149 deletions

View File

@@ -4,10 +4,10 @@ setlocal EnableDelayedExpansion
:: Set Windows Terminal installation paths. (Default and Scoop installation) :: Set Windows Terminal installation paths. (Default and Scoop installation)
set "wtDefaultPath=%LOCALAPPDATA%\Microsoft\WindowsApps\wt.exe" set "wtDefaultPath=%LOCALAPPDATA%\Microsoft\WindowsApps\wt.exe"
set "wtScoopPath=%USERPROFILE%\scoop\apps\windows-terminal\current\wt.exe" set "wtScoopPath=%USERPROFILE%\scoop\apps\windows-terminal\current\wt.exe"
set "logFile=%~dp0Logs\Win11Debloat-Run.log" set "logFile=%LOCALAPPDATA%\Win11Debloat\Logs\Win11Debloat-Run.log"
:: Ensure Logs folder exists :: Ensure Logs folder exists
if not exist "%~dp0Logs" mkdir "%~dp0Logs" if not exist "%LOCALAPPDATA%\Win11Debloat\Logs" mkdir "%LOCALAPPDATA%\Win11Debloat\Logs"
:: Determine which terminal exists :: Determine which terminal exists
if exist "%wtDefaultPath%" ( if exist "%wtDefaultPath%" (

View File

@@ -113,7 +113,12 @@ function ReplaceStartMenu {
return return
} }
$backupBinFile = $startMenuBinFile + ".bak" $startMenuBackupsDir = Join-Path $script:AppDataPath 'Backups'
if (-not (Test-Path $startMenuBackupsDir)) {
New-Item -ItemType Directory -Path $startMenuBackupsDir -Force | Out-Null
}
$backupTimestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$backupBinFile = Join-Path $startMenuBackupsDir "Win11Debloat-Start2BinBackup-$userName-$backupTimestamp.bak"
if (Test-Path $startMenuBinFile) { if (Test-Path $startMenuBinFile) {
# Backup current start menu file # Backup current start menu file
@@ -221,16 +226,18 @@ function RestoreStartMenuFromBackup {
) )
$userName = GetStartMenuUserNameFromPath -StartMenuBinFile $StartMenuBinFile $userName = GetStartMenuUserNameFromPath -StartMenuBinFile $StartMenuBinFile
$backupTimestamp = (Get-Date).ToString('yyyyMMdd_HHmmss')
$startMenuBackupsDir = Join-Path $script:AppDataPath 'Backups'
$backupBinFile = if ([string]::IsNullOrWhiteSpace($BackupFilePath)) { $backupBinFile = if ([string]::IsNullOrWhiteSpace($BackupFilePath)) {
$StartMenuBinFile + '.bak' Join-Path $startMenuBackupsDir "Win11Debloat-Start2BinBackup-$userName-$backupTimestamp.bak"
} }
else { else {
$BackupFilePath $BackupFilePath
} }
$currentBinBackup = $StartMenuBinFile + '.restore.bak' $currentBinBackup = Join-Path $startMenuBackupsDir "Win11Debloat-Start2BinRestore-$userName-$backupTimestamp.bak"
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 $BackupFilePath" -ForegroundColor Cyan
return [PSCustomObject]@{ return [PSCustomObject]@{
UserName = $userName UserName = $userName
Result = $true Result = $true
@@ -238,11 +245,11 @@ function RestoreStartMenuFromBackup {
} }
} }
if (-not (Test-Path -LiteralPath $backupBinFile)) { if (-not (Test-Path -LiteralPath $BackupFilePath)) {
return [PSCustomObject]@{ return [PSCustomObject]@{
UserName = $userName UserName = $userName
Result = $false Result = $false
Message = "No start menu backup file found for user $userName." Message = "Start menu backup file not found: $BackupFilePath"
} }
} }
@@ -251,7 +258,7 @@ function RestoreStartMenuFromBackup {
Move-Item -Path $StartMenuBinFile -Destination $currentBinBackup -Force Move-Item -Path $StartMenuBinFile -Destination $currentBinBackup -Force
} }
Copy-Item -Path $backupBinFile -Destination $StartMenuBinFile -Force Copy-Item -Path $BackupFilePath -Destination $StartMenuBinFile -Force
return [PSCustomObject]@{ return [PSCustomObject]@{
UserName = $userName UserName = $userName
Result = $true Result = $true
@@ -278,17 +285,14 @@ function RestoreStartMenuFromBackup {
be resolved. 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.
the .bak file alongside the current start2.bin.
.EXAMPLE .EXAMPLE
RestoreStartMenu RestoreStartMenu -BackupFilePath "$env:LOCALAPPDATA\Win11Debloat\Backups\Win11Debloat-Start2BinBackup-Jeff-20260623_143000.bak"
.EXAMPLE
RestoreStartMenu -BackupFilePath "C:\Backups\start2.bin"
#> #>
function RestoreStartMenu { function RestoreStartMenu {
param( param(
[Parameter(Mandatory)]
[string]$BackupFilePath [string]$BackupFilePath
) )
@@ -315,22 +319,19 @@ 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 specified backup file. For the Default user profile,
the start2.bin file (which was previously copied from a template) so removes the start2.bin file (which was previously copied from a template)
that new profiles revert to the system default start menu. 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.
the .bak file alongside each user's current start2.bin.
.EXAMPLE .EXAMPLE
RestoreStartMenuForAllUsers RestoreStartMenuForAllUsers -BackupFilePath "$env:LOCALAPPDATA\Win11Debloat\Backups\Win11Debloat-Start2BinBackup-Jeff-20260623_143000.bak"
.EXAMPLE
RestoreStartMenuForAllUsers -BackupFilePath "C:\Backups\start2.bin"
#> #>
function RestoreStartMenuForAllUsers { function RestoreStartMenuForAllUsers {
param( param(
[Parameter(Mandatory)]
[string]$BackupFilePath [string]$BackupFilePath
) )

View File

@@ -159,7 +159,7 @@
$menuReportBug.Add_Click({ Start-Process "https://github.com/Raphire/Win11Debloat/issues" }) $menuReportBug.Add_Click({ Start-Process "https://github.com/Raphire/Win11Debloat/issues" })
$menuLogs.Add_Click({ $menuLogs.Add_Click({
$logsFolder = Join-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) 'Logs' $logsFolder = Split-Path $script:DefaultLogPath -Parent
if (Test-Path $logsFolder) { if (Test-Path $logsFolder) {
Start-Process "explorer.exe" -ArgumentList $logsFolder Start-Process "explorer.exe" -ArgumentList $logsFolder
} }

View File

@@ -61,6 +61,7 @@ function Show-RestoreBackupDialog {
$startMenuIntroPanel = $window.FindName('StartMenuIntroPanel') $startMenuIntroPanel = $window.FindName('StartMenuIntroPanel')
$startMenuScopeCombo = $window.FindName('StartMenuScopeCombo') $startMenuScopeCombo = $window.FindName('StartMenuScopeCombo')
$startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck') $startMenuAutoBackupCheck = $window.FindName('StartMenuAutoBackupCheck')
$startMenuAutoBackupCheck.Visibility = 'Collapsed'
$introInfoPanel = $window.FindName('IntroInfoPanel') $introInfoPanel = $window.FindName('IntroInfoPanel')
$overviewPanel = $window.FindName('OverviewPanel') $overviewPanel = $window.FindName('OverviewPanel')
$overviewFeaturesSection = $window.FindName('OverviewFeaturesSection') $overviewFeaturesSection = $window.FindName('OverviewFeaturesSection')
@@ -148,9 +149,8 @@ function Show-RestoreBackupDialog {
return return
} }
$isAutoBackupEnabled = ($startMenuAutoBackupCheck.IsChecked -eq $true) $hasSelectedFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)
$hasSelectedManualFile = -not [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath) if ($hasSelectedFile) {
if ($isAutoBackupEnabled -or $hasSelectedManualFile) {
$primaryActionBtn.Content = 'Restore backup' $primaryActionBtn.Content = 'Restore backup'
} }
else { else {
@@ -302,15 +302,11 @@ function Show-RestoreBackupDialog {
} }
$handleStartMenuPrimaryAction = { $handleStartMenuPrimaryAction = {
$scope = (& $getStartMenuScopeInfo).Scope if ([string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
$useManualBackupFile = -not ($startMenuAutoBackupCheck.IsChecked -eq $true)
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($state.SelectedStartMenuBackupFilePath)) {
$openDialog = New-Object Microsoft.Win32.OpenFileDialog $openDialog = New-Object Microsoft.Win32.OpenFileDialog
$openDialog.Title = 'Select Start Menu Backup File' $openDialog.Title = 'Select Start Menu Backup File'
$openDialog.Filter = 'Start Menu backup (*.bak)|*.bak' $openDialog.Filter = 'Start Menu backup (*.bak)|*.bak'
$openDialog.InitialDirectory = "$env:LOCALAPPDATA\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState" $openDialog.InitialDirectory = (Join-Path $script:AppDataPath 'Backups')
$openDialog.DefaultExt = '.bak'
if ($openDialog.ShowDialog($window) -ne $true) { if ($openDialog.ShowDialog($window) -ne $true) {
return return
@@ -322,34 +318,10 @@ function Show-RestoreBackupDialog {
return return
} }
if (-not $useManualBackupFile) { $scope = (& $getStartMenuScopeInfo).Scope
$autoBackupExists = $false
if ($scope -eq 'AllUsers') {
$userPathString = GetUserDirectory -userName "*" -fileName "AppData\Local\Packages\Microsoft.Windows.StartMenuExperienceHost_cw5n1h2txyewy\LocalState"
$usersStartMenuPaths = Get-ChildItem -Path $userPathString -ErrorAction SilentlyContinue
foreach ($startMenuPath in $usersStartMenuPaths) {
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
}
}
$window.Tag = @{ $window.Tag = @{
Result = 'RestoreStartMenu' Result = 'RestoreStartMenu'
StartMenuScope = $scope StartMenuScope = $scope
UseManualBackupFile = $useManualBackupFile
BackupFilePath = $state.SelectedStartMenuBackupFilePath BackupFilePath = $state.SelectedStartMenuBackupFilePath
} }
$window.DialogResult = $true $window.DialogResult = $true
@@ -368,14 +340,6 @@ function Show-RestoreBackupDialog {
} }
} }
$startMenuAutoBackupCheck.Add_Checked({
$state.SelectedStartMenuBackupFilePath = $null
& $refreshStartMenuUi
})
$startMenuAutoBackupCheck.Add_Unchecked({
& $refreshStartMenuUi
})
$startMenuScopeCombo.Add_SelectionChanged({ $startMenuScopeCombo.Add_SelectionChanged({
& $refreshStartMenuUi & $refreshStartMenuUi
}) })
@@ -410,7 +374,6 @@ function Show-RestoreBackupDialog {
if ($state.WizardStep -eq 'StartMenu') { if ($state.WizardStep -eq 'StartMenu') {
$state.SelectedStartMenuBackupFilePath = $null $state.SelectedStartMenuBackupFilePath = $null
$startMenuAutoBackupCheck.IsChecked = $true
} }
& $setWizardStep 'SelectType' & $setWizardStep 'SelectType'

View File

@@ -40,7 +40,6 @@ function Show-RestoreBackupWindow {
} }
elseif ($dialogResult.Result -eq 'RestoreStartMenu') { elseif ($dialogResult.Result -eq 'RestoreStartMenu') {
$scope = $dialogResult.StartMenuScope $scope = $dialogResult.StartMenuScope
$useManualBackupFile = ($dialogResult.UseManualBackupFile -eq $true)
$backupFilePath = $null $backupFilePath = $null
if ($dialogResult -is [hashtable] -and $dialogResult.ContainsKey('BackupFilePath')) { if ($dialogResult -is [hashtable] -and $dialogResult.ContainsKey('BackupFilePath')) {
$backupFilePath = $dialogResult['BackupFilePath'] $backupFilePath = $dialogResult['BackupFilePath']
@@ -49,7 +48,7 @@ function Show-RestoreBackupWindow {
$backupFilePath = $dialogResult.BackupFilePath $backupFilePath = $dialogResult.BackupFilePath
} }
if ($useManualBackupFile -and [string]::IsNullOrWhiteSpace($backupFilePath)) { if ([string]::IsNullOrWhiteSpace($backupFilePath)) {
throw 'Start Menu restore canceled: no backup file selected.' throw 'Start Menu restore canceled: no backup file selected.'
} }

View File

@@ -131,31 +131,12 @@ catch {
Exit Exit
} }
# Remove old script folder if it exists, but keep configs, logs and backups # Remove old script folder if it exists
if (Test-Path $tempWorkPath) { if (Test-Path $tempWorkPath) {
Write-Output "" Write-Output ""
Write-Output "> Cleaning up old Win11Debloat folder..." Write-Output "> Cleaning up old Win11Debloat folder..."
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force Remove-Item $tempWorkPath -Recurse -Force
}
$configDir = Join-Path $tempWorkPath 'Config'
$backupDir = Join-Path $tempWorkPath 'ConfigOld'
# Temporarily move existing config files if they exist to prevent them from being overwritten by the new script files, will be moved back after the new script is unpacked
if (Test-Path "$configDir") {
Write-Output ""
Write-Output "> Backing up existing config files..."
New-Item -ItemType Directory -Path "$backupDir" -Force | Out-Null
$filesToKeep = @(
'LastUsedSettings.json'
)
Get-ChildItem -Path "$configDir" -Recurse | Where-Object { $_.Name -in $filesToKeep } | Move-Item -Destination "$backupDir"
Remove-Item "$configDir" -Recurse -Force
} }
Write-Output "" Write-Output ""
@@ -170,19 +151,6 @@ Remove-Item $tempArchivePath
# Move files # Move files
Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath
# Add existing config files back to Config folder
if (Test-Path "$backupDir") {
if (-not (Test-Path "$configDir")) {
New-Item -ItemType Directory -Path "$configDir" -Force | Out-Null
}
Write-Output ""
Write-Output "> Restoring existing config files..."
Get-ChildItem -Path "$backupDir" -Recurse | Move-Item -Destination "$configDir"
Remove-Item "$backupDir" -Recurse -Force
}
# Make list of arguments to pass on to the script # Make list of arguments to pass on to the script
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object { $arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
if ($_.Value -eq $true) { if ($_.Value -eq $true) {
@@ -219,13 +187,12 @@ if ($null -ne $debloatProcess) {
$debloatProcess.WaitForExit() $debloatProcess.WaitForExit()
} }
# Remove all remaining script files, except for configs, logs and backups # Remove all remaining script files
if (Test-Path $tempWorkPath) { if (Test-Path $tempWorkPath) {
Write-Output "" Write-Output ""
Write-Output "> Cleaning up..." Write-Output "> Cleaning up..."
# Cleanup, remove Win11Debloat directory Remove-Item $tempWorkPath -Recurse -Force
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force
} }
Write-Output "" Write-Output ""

View File

@@ -132,31 +132,12 @@ catch {
Exit Exit
} }
# Remove old script folder if it exists, but keep configs, logs and backups # Remove old script folder if it exists
if (Test-Path $tempWorkPath) { if (Test-Path $tempWorkPath) {
Write-Output "" Write-Output ""
Write-Output "> Cleaning up old Win11Debloat folder..." Write-Output "> Cleaning up old Win11Debloat folder..."
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force Remove-Item $tempWorkPath -Recurse -Force
}
$configDir = Join-Path $tempWorkPath 'Config'
$backupDir = Join-Path $tempWorkPath 'ConfigOld'
# Temporarily move existing config files if they exist to prevent them from being overwritten by the new script files, will be moved back after the new script is unpacked
if (Test-Path "$configDir") {
Write-Output ""
Write-Output "> Backing up existing config files..."
New-Item -ItemType Directory -Path "$backupDir" -Force | Out-Null
$filesToKeep = @(
'LastUsedSettings.json'
)
Get-ChildItem -Path "$configDir" -Recurse | Where-Object { $_.Name -in $filesToKeep } | Move-Item -Destination "$backupDir"
Remove-Item "$configDir" -Recurse -Force
} }
Write-Output "" Write-Output ""
@@ -171,19 +152,6 @@ Remove-Item $tempArchivePath
# Move files # Move files
Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath Get-ChildItem -Path (Join-Path $tempWorkPath '*Win11Debloat-*') -Recurse | Move-Item -Destination $tempWorkPath
# Add existing config files back to Config folder
if (Test-Path "$backupDir") {
if (-not (Test-Path "$configDir")) {
New-Item -ItemType Directory -Path "$configDir" -Force | Out-Null
}
Write-Output ""
Write-Output "> Restoring existing config files..."
Get-ChildItem -Path "$backupDir" -Recurse | Move-Item -Destination "$configDir"
Remove-Item "$backupDir" -Recurse -Force
}
# Make list of arguments to pass on to the script # Make list of arguments to pass on to the script
$arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object { $arguments = $($PSBoundParameters.GetEnumerator() | ForEach-Object {
if ($_.Value -eq $true) { if ($_.Value -eq $true) {
@@ -220,13 +188,12 @@ if ($null -ne $debloatProcess) {
$debloatProcess.WaitForExit() $debloatProcess.WaitForExit()
} }
# Remove all remaining script files, except for configs, logs and backups # Remove all remaining script files
if (Test-Path $tempWorkPath) { if (Test-Path $tempWorkPath) {
Write-Output "" Write-Output ""
Write-Output "> Cleaning up..." Write-Output "> Cleaning up..."
# Cleanup, remove Win11Debloat directory Remove-Item $tempWorkPath -Recurse -Force
Get-ChildItem -Path $tempWorkPath -Exclude Config,Logs,Backups | Remove-Item -Recurse -Force
} }
Write-Output "" Write-Output ""

View File

@@ -139,17 +139,17 @@ if (-not $isAdmin) {
# Define script-level variables & paths # Define script-level variables & paths
$script:Version = "2026.06.14" $script:Version = "2026.06.14"
$configPath = Join-Path $PSScriptRoot 'Config' $configPath = Join-Path $PSScriptRoot 'Config'
$logsPath = Join-Path $PSScriptRoot 'Logs'
$schemasPath = Join-Path $PSScriptRoot 'Schemas' $schemasPath = Join-Path $PSScriptRoot 'Schemas'
$scriptsPath = Join-Path $PSScriptRoot 'Scripts' $scriptsPath = Join-Path $PSScriptRoot 'Scripts'
$script:AppDataPath = Join-Path $env:LOCALAPPDATA 'Win11Debloat'
$script:AppsListFilePath = Join-Path $configPath 'Apps.json' $script:AppsListFilePath = Join-Path $configPath 'Apps.json'
$script:DefaultSettingsFilePath = Join-Path $configPath 'DefaultSettings.json' $script:DefaultSettingsFilePath = Join-Path $configPath 'DefaultSettings.json'
$script:FeaturesFilePath = Join-Path $configPath 'Features.json' $script:FeaturesFilePath = Join-Path $configPath 'Features.json'
$script:SavedSettingsFilePath = Join-Path $configPath 'LastUsedSettings.json' $script:SavedSettingsFilePath = Join-Path $script:AppDataPath 'LastUsedSettings.json'
$script:DefaultLogPath = Join-Path $logsPath 'Win11Debloat.log' $script:DefaultLogPath = Join-Path (Join-Path $script:AppDataPath 'Logs') 'Win11Debloat.log'
$script:RegfilesPath = Join-Path $PSScriptRoot 'Regfiles' $script:RegfilesPath = Join-Path $PSScriptRoot 'Regfiles'
$script:RegistryBackupsPath = Join-Path $PSScriptRoot 'Backups' $script:RegistryBackupsPath = Join-Path $script:AppDataPath 'Backups'
$script:AssetsPath = Join-Path $PSScriptRoot 'Assets' $script:AssetsPath = Join-Path $PSScriptRoot 'Assets'
$script:AppSelectionSchema = Join-Path $schemasPath 'AppSelectionWindow.xaml' $script:AppSelectionSchema = Join-Path $schemasPath 'AppSelectionWindow.xaml'
$script:MainWindowSchema = Join-Path $schemasPath 'MainWindow.xaml' $script:MainWindowSchema = Join-Path $schemasPath 'MainWindow.xaml'