From 3b4a5f16ad886d13c5846179e8167202843f326c Mon Sep 17 00:00:00 2001 From: HetCreep Date: Fri, 3 Jul 2026 02:16:47 +0700 Subject: [PATCH] fix(restore-gui): surface registry-backup load failures to the user (#685) Co-authored-by: Jeffrey <9938813+Raphire@users.noreply.github.com> --- Scripts/Features/RestoreRegistryBackup.ps1 | 64 +++++++++++++++++++++- Scripts/GUI/Show-RestoreBackupDialog.ps1 | 32 ++++++++++- 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/Scripts/Features/RestoreRegistryBackup.ps1 b/Scripts/Features/RestoreRegistryBackup.ps1 index 0d56269..7c30f4e 100644 --- a/Scripts/Features/RestoreRegistryBackup.ps1 +++ b/Scripts/Features/RestoreRegistryBackup.ps1 @@ -1,3 +1,19 @@ +<# + .SYNOPSIS + Loads a registry backup from a JSON file and normalizes its contents. + + .DESCRIPTION + Loads a registry backup from disk and returns a normalized representation + of its contents suitable for use by the restore workflow. Throws if the + file is missing, unreadable, or not valid JSON. + + .PARAMETER FilePath + The absolute path to the registry backup JSON file to load. + + .OUTPUTS + PSCustomObject + A normalized registry backup object produced by Normalize-RegistryBackup. +#> function Load-RegistryBackupFromFile { param( [Parameter(Mandatory)] @@ -18,6 +34,24 @@ function Load-RegistryBackupFromFile { return Normalize-RegistryBackup -Backup $rawBackup } +<# + .SYNOPSIS + Validates and normalizes a raw registry backup object. + + .DESCRIPTION + Validates the structure and content of the supplied backup and converts + it into a normalized representation that can be safely consumed by the + restore workflow. Throws if validation fails. + + .PARAMETER Backup + The raw backup object (typically parsed from JSON) to normalize. + + .OUTPUTS + PSCustomObject + A normalized backup with Version, BackupType, CreatedAt, CreatedBy, + ComputerName, Target, SelectedFeatures, SelectedUndoFeatures, and + RegistryKeys properties. +#> function Normalize-RegistryBackup { param( [Parameter(Mandatory)] @@ -97,9 +131,16 @@ function Normalize-RegistryBackup { if ($allSelectedFeatures.Count -eq 0) { $errors.Add('Backup must contain at least one feature ID in SelectedFeatures or SelectedUndoFeatures.') } - $allowListValidationErrors = @(Test-RegistryBackupMatchesSelectedFeatures -SelectedFeatureIds @($selectedFeatures) -SelectedUndoFeatureIds @($selectedUndoFeatures) -Target $normalizedTarget -RegistryKeys @($normalizedKeys)) - foreach ($allowListValidationError in $allowListValidationErrors) { - $errors.Add([string]$allowListValidationError) + else { + try { + $allowListValidationErrors = @(Test-RegistryBackupMatchesSelectedFeatures -SelectedFeatureIds @($selectedFeatures) -SelectedUndoFeatureIds @($selectedUndoFeatures) -Target $normalizedTarget -RegistryKeys @($normalizedKeys)) + foreach ($allowListValidationError in $allowListValidationErrors) { + $errors.Add([string]$allowListValidationError) + } + } + catch { + $errors.Add("Failed to validate backup: $($_.Exception.Message)") + } } if ($errors.Count -gt 0) { @@ -125,6 +166,23 @@ function Normalize-RegistryBackup { } } +<# + .SYNOPSIS + Restores registry state from a normalized backup object. + + .DESCRIPTION + Applies the registry state described by the supplied backup back to the + registry, loading the appropriate user hive when required. + + .PARAMETER Backup + A normalized backup object (as produced by Normalize-RegistryBackup) whose + RegistryKeys snapshots should be restored. + + .OUTPUTS + PSCustomObject + Returns an object with a Result property set to $true when the restore + completes successfully. +#> function Restore-RegistryBackupState { param( [Parameter(Mandatory)] diff --git a/Scripts/GUI/Show-RestoreBackupDialog.ps1 b/Scripts/GUI/Show-RestoreBackupDialog.ps1 index 8cacede..054fb15 100644 --- a/Scripts/GUI/Show-RestoreBackupDialog.ps1 +++ b/Scripts/GUI/Show-RestoreBackupDialog.ps1 @@ -1,3 +1,24 @@ +<# + .SYNOPSIS + Displays the Restore Backup wizard dialog. + + .DESCRIPTION + Presents a modal wizard that lets the user choose and restore either a + registry backup or a Start Menu pinned-apps backup. Returns the user's + selection via $window.Tag. + + .PARAMETER Owner + Optional parent WPF Window used to host this modal dialog. Defaults to the + shared $script:GuiWindow when not supplied. + + .OUTPUTS + Hashtable + Returns a Hashtable describing the user's choice. Possible shapes: + RestoreRegistry - @{ Result='RestoreRegistry'; Backup= } + RestoreStartMenu - @{ Result='RestoreStartMenu'; StartMenuScope=; + UseManualBackupFile=; BackupFilePath= } + Cancelled - @{ Result='Cancelled' } (from New-RestoreDialogState) +#> function Show-RestoreBackupDialog { param( [System.Windows.Window]$Owner = $null @@ -295,9 +316,16 @@ function Show-RestoreBackupDialog { } Write-Host "Backup file selected: $($openDialog.FileName)" - $selectedBackup = Load-RegistryBackupFromFile -FilePath $openDialog.FileName - if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) { + try { + $selectedBackup = Load-RegistryBackupFromFile -FilePath $openDialog.FileName + + if (-not (& $showRegistryOverview -SelectedBackup $selectedBackup -SelectedBackupFilePath $openDialog.FileName)) { + return + } + } + catch { + Show-MessageBox -Owner $window -Title 'Invalid Backup File' -Message "The selected file could not be loaded:`n$($_.Exception.Message)" -Button 'OK' -Icon 'Error' | Out-Null return }