From a104cfdebb7663b75afeaeaea7f698b38de0aed8 Mon Sep 17 00:00:00 2001 From: cproudlock Date: Sun, 24 May 2026 07:29:27 -0400 Subject: [PATCH] Wax/Trace triad: fix registry corruption + cover v6.213 vendor install path Three fixes in Backup / Export / Install, validated end-to-end on the win11 VM against a seeded HKCU\SOFTWARE\Mitutoyo\Formtracepak key carrying all five registry value types (String, DWord, ExpandString, MultiString, Binary). 1. Registry corruption on REG_BINARY / REG_MULTI_SZ restore Backup wrote those values to registry_values.csv via [string]$val, which lossily coerces a byte[] to "System.Byte[]" and a string[] to a space-joined scalar. Install's CSV restore loop runs AFTER the .reg file import (which is lossless), so the CSV pass overwrites the good values with corrupted strings. Two-part fix: - Backup: skip Binary / MultiString / None / Unknown when writing the CSV. Only String, ExpandString, DWord, QWord roundtrip cleanly through New-ItemProperty -PropertyType, so capture only those. The .reg file remains authoritative for the rest. - Install: defensive filter on the CSV restore loop that skips any row whose Type is not in {String, ExpandString, DWord, QWord}. This catches legacy CSVs already on the share that were taken before this fix. 2. v6.213 vendor install path not scanned / not restored to The per-bay FormTracePak install (commit 54dddaa) lands under C:\Program Files (x86)\MitutoyoApp\Formtracepak, but the search-path lists in Backup + Export only covered C:\...\Mitutoyo (no MitutoyoApp). Result: a backup taken on a freshly imaged v6.213 bay produced Config Files = 0 because the script never looked at the actual install dir. Added MitutoyoApp (x86 + native ProgramFiles) ahead of the legacy paths in all three scripts. 3. Install $DefaultAppTargets fallback didn't include MitutoyoApp either, so a restore from an OLDER bay (source path C:\Mitutoyo\...) onto a freshly imaged v6.213 bay would fall back to ProgramFiles\Mitutoyo (does not exist), miss the MitutoyoApp\Formtracepak tree, and write the restored files into the first existing legacy path. Added the MitutoyoApp entries at the top of the ordered fallback table. Smoke tested on win11 VM: backup of all 5 reg types, then corrupt every value, then Install -RestoreAll restores all 5 byte-exact (incl. REG_BINARY DE-AD-BE-EF-CA-FE-BA-BE-01-02-03-04 and REG_MULTI_SZ alpha.smp/beta.smp/ gamma.smp). Verified legacy poison-CSV path triggers the defensive filter and the .reg-imported values survive untouched. -DryRun confirmed non-mutating. Idempotency confirmed via hash-skip. --- .../scripts/Backup-FormtracepakSettings.ps1 | 20 +++++++++++++++++-- .../scripts/Export-FormtracepakInventory.ps1 | 3 +++ .../scripts/Install-FormtracepakSettings.ps1 | 19 +++++++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Backup-FormtracepakSettings.ps1 b/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Backup-FormtracepakSettings.ps1 index 5e050e7..dafd8de 100755 --- a/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Backup-FormtracepakSettings.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Backup-FormtracepakSettings.ps1 @@ -84,6 +84,9 @@ $ErrorActionPreference = 'Continue' # ---------------------------------------------------------------------- $ApplicationPaths = @( + # v6.213 vendor MSI layout (the path the per-bay install lands on today). + "${env:ProgramFiles(x86)}\MitutoyoApp" + "${env:ProgramFiles}\MitutoyoApp" "${env:ProgramFiles}\Mitutoyo" "${env:ProgramFiles(x86)}\Mitutoyo" "${env:ProgramFiles}\FORMTRACEPAK" @@ -280,15 +283,28 @@ foreach ($root in $RegistryRoots) { Write-Warning " reg.exe export failed for ${root}: $_" } - # Also capture as CSV for granular restore + # Also capture as CSV for granular restore. + # + # CSV path only round-trips cleanly for String / ExpandString / DWord / + # QWord. Binary and MultiString lossily coerce through [string] (Binary + # becomes "System.Byte[]" literal; MultiString becomes space-joined), + # which then corrupts the registry if the CSV restore overwrites the + # .reg-import result. Skip those types in the CSV (the .reg file is + # authoritative for them). + $csvSafeTypes = @('String', 'ExpandString', 'DWord', 'QWord') try { $allKeys = @(Get-Item -Path $root -ErrorAction SilentlyContinue) $allKeys += Get-ChildItem -Path $root -Recurse -ErrorAction SilentlyContinue foreach ($key in $allKeys) { foreach ($valName in $key.GetValueNames()) { - $val = $key.GetValue($valName) $kind = $key.GetValueKind($valName) + if ($csvSafeTypes -notcontains [string]$kind) { + # Skip Binary / MultiString / None / Unknown. The .reg + # export already captured them losslessly. + continue + } + $val = $key.GetValue($valName) $regCsvRows.Add([PSCustomObject]@{ Path = $key.PSPath Name = $valName diff --git a/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Export-FormtracepakInventory.ps1 b/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Export-FormtracepakInventory.ps1 index d5ce56a..5bd53f1 100755 --- a/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Export-FormtracepakInventory.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Export-FormtracepakInventory.ps1 @@ -34,6 +34,9 @@ $ErrorActionPreference = 'Continue' # ---------------------------------------------------------------------- $ApplicationSearchPaths = @( + # v6.213 vendor MSI layout (the path the per-bay install lands on today). + "${env:ProgramFiles(x86)}\MitutoyoApp" + "${env:ProgramFiles}\MitutoyoApp" "${env:ProgramFiles}\Mitutoyo" "${env:ProgramFiles(x86)}\Mitutoyo" "${env:ProgramFiles}\FORMTRACEPAK" diff --git a/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Install-FormtracepakSettings.ps1 b/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Install-FormtracepakSettings.ps1 index 6423a20..ca1d0d0 100755 --- a/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Install-FormtracepakSettings.ps1 +++ b/playbook/shopfloor-setup/gea-shopfloor-waxtrace/scripts/Install-FormtracepakSettings.ps1 @@ -91,6 +91,11 @@ $ErrorActionPreference = 'Continue' # ---------------------------------------------------------------------- $DefaultAppTargets = [ordered]@{ + # v6.213 vendor MSI installs under MitutoyoApp\Formtracepak\. Order this + # first so restored config under e.g. C:\Mitutoyo\Formtracepak\ from an + # older bay lands in the right tree on a freshly imaged v6.213 bay. + 'MitutoyoAppX86' = "${env:ProgramFiles(x86)}\MitutoyoApp" + 'MitutoyoApp' = "${env:ProgramFiles}\MitutoyoApp" 'ProgramFiles' = "${env:ProgramFiles}\Mitutoyo" 'ProgramFilesX86' = "${env:ProgramFiles(x86)}\Mitutoyo" 'CMitutoyo' = 'C:\Mitutoyo' @@ -347,7 +352,13 @@ if ($RestoreRegistry) { $counters.RegistryKeys++ } - # Method 2: CSV-based key-by-key restore + # Method 2: CSV-based key-by-key restore. CSV captures String / + # ExpandString / DWord / QWord only (Backup-FormtracepakSettings.ps1 + # skips Binary + MultiString because [string] coercion corrupts them). + # The .reg file import above is authoritative for those types. Defend + # against older CSVs (pre-fix) that carry corrupt Binary/MultiString + # rows by skipping anything that is not a CSV-safe type. + $csvSafeTypes = @('String', 'ExpandString', 'DWord', 'QWord') $regCsv = Join-Path $regDir 'registry_values.csv' if (Test-Path $regCsv) { $regEntries = Import-Csv $regCsv @@ -357,6 +368,12 @@ if ($RestoreRegistry) { $regValue = $re.Value $regType = $re.Type + if ($regType -and ($csvSafeTypes -notcontains $regType)) { + Write-Host " [SKIP] Non-CSV-safe type '$regType' at ${regPath}\$regName (the .reg file import handled it)" -ForegroundColor DarkGray + $counters.Skipped++ + continue + } + if ($regPath -match '^HKLM' -and -not $isAdmin -and -not $DryRun) { Write-Host " [SKIP] HKLM key requires elevation: $regPath\$regName" -ForegroundColor DarkYellow $counters.Skipped++