From 24f10b89b0934cbd8e60141577cf3e648de0852c Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com> Date: Wed, 15 Apr 2026 13:04:12 -0700 Subject: [PATCH] Fixes DeviceNamingMode UI state tracking and defaults Updates the FFU UI core to better track the loaded `DeviceNamingMode` configuration. Introduces flags to detect explicit user changes versus default states. Ensures that if the user does not explicitly set the device naming mode, it defaults gracefully and preserves legacy script behaviors. Updates documentation to reflect the actual behavior of not writing the `DeviceNamingMode` key unless modified. --- FFUDevelopment/BuildFFUVM_UI.ps1 | 7 +- .../FFUUI.Core/FFUUI.Core.Config.psm1 | 19 +++-- .../FFUUI.Core/FFUUI.Core.Handlers.psm1 | 77 +++++++++++++++++- .../FFUUI.Core/FFUUI.Core.Initialize.psm1 | 8 +- FFUDevelopment/config/Sample_default.json | Bin 6826 -> 6760 bytes docs/build.md | 7 +- docs/parameters_reference.md | 2 +- 7 files changed, 102 insertions(+), 18 deletions(-) diff --git a/FFUDevelopment/BuildFFUVM_UI.ps1 b/FFUDevelopment/BuildFFUVM_UI.ps1 index 6f2c462..f2503d6 100644 --- a/FFUDevelopment/BuildFFUVM_UI.ps1 +++ b/FFUDevelopment/BuildFFUVM_UI.ps1 @@ -46,7 +46,8 @@ $script:uiState = [PSCustomObject]@{ logStreamReader = $null; pollTimer = $null; currentBuildProcess = $null; - lastConfigFilePath = $null + lastConfigFilePath = $null; + loadedDeviceNamingMode = $null }; Flags = @{ installAppsForcedByUpdates = $false; @@ -56,7 +57,9 @@ $script:uiState = [PSCustomObject]@{ lastSortAscending = $true; isBuilding = $false; isCleanupRunning = $false; - isFluentSupported = $false + isFluentSupported = $false; + deviceNamingModeWasExplicitlyChanged = $false; + suppressDeviceNamingChangeTracking = $false }; Defaults = @{}; LogFilePath = "$FFUDevelopmentPath\FFUDevelopment_UI.log" diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 index f9e24de..a7a5d6d 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Config.psm1 @@ -36,7 +36,7 @@ function Get-UIConfig { UseDriversAsPEDrivers = $State.Controls.chkUseDriversAsPEDrivers.IsChecked CopyPPKG = $State.Controls.chkCopyPPKG.IsChecked CopyUnattend = $State.Controls.chkCopyUnattend.IsChecked - DeviceNamingMode = Get-SelectedDeviceNamingMode -State $State + DeviceNamingMode = Get-ConfiguredDeviceNamingMode -State $State DeviceNameTemplate = $State.Controls.txtDeviceNameTemplate.Text DeviceNamePrefixesPath = $State.Controls.txtDeviceNamePrefixesPath.Text DeviceNamePrefixes = @(Get-DeviceNamePrefixes -State $State) @@ -480,17 +480,20 @@ function Update-UIFromConfig { $State.Controls.txtDeviceNamePrefixesPath.Text = Get-DefaultDeviceNamePrefixesPath -FFUDevelopmentPath $State.Controls.txtFFUDevPath.Text } - $deviceNamingMode = 'None' + $loadedDeviceNamingMode = $null if ($ConfigContent.PSObject.Properties.Name -contains 'DeviceNamingMode') { - $deviceNamingMode = [string]$ConfigContent.DeviceNamingMode + $candidateDeviceNamingMode = [string]$ConfigContent.DeviceNamingMode + if ($candidateDeviceNamingMode -in @('Legacy', 'None', 'Prompt', 'Template', 'Prefixes')) { + $loadedDeviceNamingMode = $candidateDeviceNamingMode + } } - if ($deviceNamingMode -eq 'Legacy') { - $deviceNamingMode = 'Prompt' + $displayDeviceNamingMode = if ($loadedDeviceNamingMode -in @('Prompt', 'Template', 'Prefixes')) { + $loadedDeviceNamingMode } - if ($deviceNamingMode -notin @('None', 'Prompt', 'Template', 'Prefixes')) { - $deviceNamingMode = 'None' + else { + 'None' } - Set-DeviceNamingMode -State $State -Mode $deviceNamingMode + Set-DeviceNamingModeState -State $State -DisplayMode $displayDeviceNamingMode -LoadedMode $loadedDeviceNamingMode Import-DeviceNamePrefixesFromConfiguredPath -State $State Update-DeviceNamingControls -State $State diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 index 37a0c96..6b86f6f 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Handlers.psm1 @@ -58,6 +58,55 @@ function Set-DeviceNamingMode { $State.Controls.rbDeviceNamingPrefixes.IsChecked = $Mode -eq 'Prefixes' } +function Set-DeviceNamingModeState { + param( + [PSCustomObject]$State, + [ValidateSet('None', 'Prompt', 'Template', 'Prefixes')] + [string]$DisplayMode, + [AllowNull()] + [string]$LoadedMode + ) + + if ($null -eq $State.Flags) { + $State.Flags = @{} + } + + if ($null -eq $State.Data) { + $State.Data = @{} + } + + $previousSuppressionState = $true -eq $State.Flags.suppressDeviceNamingChangeTracking + $State.Flags.suppressDeviceNamingChangeTracking = $true + try { + Set-DeviceNamingMode -State $State -Mode $DisplayMode + } + finally { + $State.Flags.suppressDeviceNamingChangeTracking = $previousSuppressionState + } + + $State.Data.loadedDeviceNamingMode = if ([string]::IsNullOrWhiteSpace($LoadedMode)) { + $null + } + else { + $LoadedMode.Trim() + } + $State.Flags.deviceNamingModeWasExplicitlyChanged = $false +} + +function Get-ConfiguredDeviceNamingMode { + param([PSCustomObject]$State) + + if (($null -ne $State.Flags) -and ($true -eq $State.Flags.deviceNamingModeWasExplicitlyChanged)) { + return Get-SelectedDeviceNamingMode -State $State + } + + if (($null -ne $State.Data) -and -not [string]::IsNullOrWhiteSpace([string]$State.Data.loadedDeviceNamingMode)) { + return [string]$State.Data.loadedDeviceNamingMode + } + + return $null +} + function Get-DeviceNamePrefixes { param([PSCustomObject]$State) @@ -467,17 +516,32 @@ function Register-EventHandlers { $State.Controls.rbDeviceNamingNone.Add_Checked({ param($eventSource, $routedEventArgs) $window = [System.Windows.Window]::GetWindow($eventSource) - Update-DeviceNamingControls -State $window.Tag + $localState = $window.Tag + if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) { + $localState.Flags.deviceNamingModeWasExplicitlyChanged = $true + $localState.Data.loadedDeviceNamingMode = $null + } + Update-DeviceNamingControls -State $localState }) $State.Controls.rbDeviceNamingPrompt.Add_Checked({ param($eventSource, $routedEventArgs) $window = [System.Windows.Window]::GetWindow($eventSource) - Update-DeviceNamingControls -State $window.Tag + $localState = $window.Tag + if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) { + $localState.Flags.deviceNamingModeWasExplicitlyChanged = $true + $localState.Data.loadedDeviceNamingMode = $null + } + Update-DeviceNamingControls -State $localState }) $State.Controls.rbDeviceNamingTemplate.Add_Checked({ param($eventSource, $routedEventArgs) $window = [System.Windows.Window]::GetWindow($eventSource) - Update-DeviceNamingControls -State $window.Tag + $localState = $window.Tag + if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) { + $localState.Flags.deviceNamingModeWasExplicitlyChanged = $true + $localState.Data.loadedDeviceNamingMode = $null + } + Update-DeviceNamingControls -State $localState }) $State.Controls.txtDeviceNameTemplate.Add_TextChanged({ param($eventSource, $textChangedEventArgs) @@ -489,7 +553,12 @@ function Register-EventHandlers { $State.Controls.rbDeviceNamingPrefixes.Add_Checked({ param($eventSource, $routedEventArgs) $window = [System.Windows.Window]::GetWindow($eventSource) - Update-DeviceNamingControls -State $window.Tag + $localState = $window.Tag + if (-not ($true -eq $localState.Flags.suppressDeviceNamingChangeTracking)) { + $localState.Flags.deviceNamingModeWasExplicitlyChanged = $true + $localState.Data.loadedDeviceNamingMode = $null + } + Update-DeviceNamingControls -State $localState }) $State.Controls.btnBrowseDeviceNamePrefixesPath.Add_Click({ param($eventSource, $routedEventArgs) diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 index 62dcdc0..43530b8 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 @@ -400,7 +400,13 @@ function Initialize-UIDefaults { $State.Controls.chkCopyAutopilot.IsChecked = $State.Defaults.generalDefaults.CopyAutopilot $State.Controls.chkCopyUnattend.IsChecked = $State.Defaults.generalDefaults.CopyUnattend $State.Controls.chkCopyPPKG.IsChecked = $State.Defaults.generalDefaults.CopyPPKG - Set-DeviceNamingMode -State $State -Mode $State.Defaults.generalDefaults.DeviceNamingMode + $defaultDeviceNamingMode = if ($State.Defaults.generalDefaults.DeviceNamingMode -in @('None', 'Prompt', 'Template', 'Prefixes')) { + $State.Defaults.generalDefaults.DeviceNamingMode + } + else { + 'None' + } + Set-DeviceNamingModeState -State $State -DisplayMode $defaultDeviceNamingMode -LoadedMode $null $State.Controls.txtDeviceNameTemplate.Text = $State.Defaults.generalDefaults.DeviceNameTemplate $State.Controls.txtDeviceNamePrefixesPath.Text = $State.Defaults.generalDefaults.DeviceNamePrefixesPath $State.Controls.txtDeviceNamePrefixes.Text = ($State.Defaults.generalDefaults.DeviceNamePrefixes -join [System.Environment]::NewLine) diff --git a/FFUDevelopment/config/Sample_default.json b/FFUDevelopment/config/Sample_default.json index dcc97ea59f3b218479d5eb387e6e203bfa03be38..09ee3809718125cf9310f12a19e6e85eae76fc66 100644 GIT binary patch delta 12 TcmZ2w`od(x57y0AYy~0!Cjtc^ delta 44 zcmaE1vdVPB4_5I^hCGIJ249AJh7^WW1|*` for this default behavior. +- If you leave device naming untouched, FFU Builder does not write `DeviceNamingMode` to the generated config. This preserves the script's `Legacy` default, so an existing `FFUDevelopment\Unattend\prefixes.txt` file is still copied to deployment media when present. +- If you explicitly select this option, FFU Builder writes `DeviceNamingMode = None`. The unattend file is still applied, but Windows generates a random computer name instead of forcing a prompt or a fixed name. + +The active `unattend_*.xml` files in `FFUDevelopment\Unattend` use `*` in the current sample files. ### Prompt for Device Name diff --git a/docs/parameters_reference.md b/docs/parameters_reference.md index ec31da0..400f89b 100644 --- a/docs/parameters_reference.md +++ b/docs/parameters_reference.md @@ -41,7 +41,7 @@ This table lists all top-level parameters in BuildFFUVM.ps1. | -CopyUnattend | bool | Copy Unattend.xml | When set to $true, stages the selected architecture-specific unattend XML file as Unattend.xml on the Deployment partition of the USB drive. Cannot be used together with -InjectUnattend. Default is $false. | | -CreateDeploymentMedia | bool | Create Deployment Media | When set to $true, this will create WinPE deployment media for use when deploying to a physical device. | | -CustomFFUNameTemplate | string | Custom FFU Name Template | Sets a custom FFU output name with placeholders. Allowed placeholders are: {WindowsRelease}, {WindowsVersion}, {SKU}, {BuildDate}, {yyyy}, {MM}, {dd}, {H}, {hh}, {mm}, {tt}. | -| -DeviceNamingMode | string | Device Naming expander | Controls how device naming is handled when unattend content is copied to USB media or injected into the FFU. Accepted values are Legacy, None, Prompt, Template, and Prefixes. The UI uses None, Prompt, Template, and Prefixes. Prompt rewrites the staged deployment unattend to the existing manual prompt placeholder and requires -CopyUnattend. | +| -DeviceNamingMode | string | Device Naming expander | Controls how device naming is handled when unattend content is copied to USB media or injected into the FFU. Accepted values are Legacy, None, Prompt, Template, and Prefixes. The UI shows None, Prompt, Template, and Prefixes. When device naming is left untouched in the UI, the generated config does not write DeviceNamingMode, which preserves the script default of Legacy. Prompt rewrites the staged deployment unattend to the existing manual prompt placeholder and requires -CopyUnattend. | | -DeviceNameTemplate | string | Specify Device Name | Sets the device name used when DeviceNamingMode is Template. Supports a static name or the %serial% token when -CopyUnattend is used. | | -DeviceNamePrefixesPath | string | Prefixes File Path | Path to the source prefixes file used for legacy copy or when -DeviceNamePrefixes is not supplied. Default is $FFUDevelopmentPath\Unattend\prefixes.txt. | | -UnattendX64FilePath | string | x64 Unattend File Path | Path to the x64 unattend XML source file used by Copy Unattend.xml and Inject Unattend.xml. Default is $FFUDevelopmentPath\Unattend\unattend_x64.xml. |