Public/Set-MSIXActivePSFFramework.ps1
|
function Set-MSIXActivePSFFramework { <# .SYNOPSIS Sets the active PSF (Package Support Framework) used by all PSF cmdlets. .DESCRIPTION Discovers all PSF installations under the module MSIXPSF folder by searching for directories that contain PsfLauncher64.exe or PsfLauncher32.exe. Sets the module-scope variable $Script:PsfBasePath so that Add-MSIXPsfFrameworkFiles and related cmdlets automatically use the correct files. Discovered options include: - MicrosoftPSF (Microsoft official PSF) - TimManganPSF (Tim Mangan PSF root with existing files) - TimManganPSF\2026-2-22_release (specific downloaded release build) - TimManganPSF\2026-2-22_debug (specific downloaded debug build) Use -List to display all available installations without changing the active one. Tab-completion on -Framework enumerates valid options automatically. .PARAMETER Framework Relative path under the MSIXPSF folder identifying the PSF to activate. Examples: "MicrosoftPSF", "TimManganPSF", "TimManganPSF\2026-2-22_release" .PARAMETER List Lists all discovered PSF installations with their full paths. Does not change the active framework. .EXAMPLE Set-MSIXActivePSFFramework -Framework MicrosoftPSF .EXAMPLE Set-MSIXActivePSFFramework -Framework "TimManganPSF\2026-2-22_release" .EXAMPLE Set-MSIXActivePSFFramework -List .NOTES https://www.nick-it.de Andreas Nick, 2026 #> [CmdletBinding(DefaultParameterSetName = 'Set')] param( [Parameter(Mandatory = $true, ParameterSetName = 'Set', Position = 0)] [ArgumentCompleter({ param($cmd, $param, $word, $ast, $fakeBound) # Access the module-private variable from within the module scope $module = Get-Module MSIXForcelets if ($null -eq $module) { return } $psfRoot = & $module { $Script:MSIXPSFPath } if (-not (Test-Path $psfRoot)) { return } $options = @() foreach ($dir in (Get-ChildItem $psfRoot -Directory -ErrorAction SilentlyContinue)) { $hasLauncher = (Test-Path (Join-Path $dir.FullName 'PsfLauncher64.exe')) -or (Test-Path (Join-Path $dir.FullName 'PsfLauncher32.exe')) if ($hasLauncher) { $options += $dir.Name } # One level deeper (e.g. TimManganPSF\2026-2-22_release) foreach ($sub in (Get-ChildItem $dir.FullName -Directory -ErrorAction SilentlyContinue)) { $subHas = (Test-Path (Join-Path $sub.FullName 'PsfLauncher64.exe')) -or (Test-Path (Join-Path $sub.FullName 'PsfLauncher32.exe')) if ($subHas) { $options += "$($dir.Name)\$($sub.Name)" } } } # Quote options that contain a backslash so PowerShell handles them correctly $options | Where-Object { $_ -like "$word*" } | ForEach-Object { if ($_ -match '\\') { "'$_'" } else { $_ } } })] [string] $Framework, [Parameter(Mandatory = $true, ParameterSetName = 'List')] [Switch] $List ) $psfRoot = $Script:MSIXPSFPath if (-not (Test-Path $psfRoot)) { Write-Error "MSIXPSF folder not found: $psfRoot" return } # Discover all valid PSF installations (top-level and one sublevel) $discovered = @() foreach ($dir in (Get-ChildItem $psfRoot -Directory -ErrorAction SilentlyContinue)) { $hasLauncher = (Test-Path (Join-Path $dir.FullName 'PsfLauncher64.exe')) -or (Test-Path (Join-Path $dir.FullName 'PsfLauncher32.exe')) if ($hasLauncher) { $discovered += [PSCustomObject]@{ Name = $dir.Name Path = $dir.FullName } } foreach ($sub in (Get-ChildItem $dir.FullName -Directory -ErrorAction SilentlyContinue)) { $subHas = (Test-Path (Join-Path $sub.FullName 'PsfLauncher64.exe')) -or (Test-Path (Join-Path $sub.FullName 'PsfLauncher32.exe')) if ($subHas) { $discovered += [PSCustomObject]@{ Name = "$($dir.Name)\$($sub.Name)" Path = $sub.FullName } } } } # -List: display available installations and return if ($PSCmdlet.ParameterSetName -eq 'List') { if ($discovered.Count -eq 0) { Write-Warning "No PSF installations found under $psfRoot" return } $active = $Script:PsfBasePath $discovered | ForEach-Object { $marker = if ($_.Path -eq $active) { '*' } else { ' ' } [PSCustomObject]@{ Active = $marker Name = $_.Name Path = $_.Path } } | Format-Table -AutoSize return } # -Framework: find and activate the selected installation $selected = $discovered | Where-Object { $_.Name -eq $Framework } | Select-Object -First 1 # A family name (e.g. 'TimManganPSF' or 'MicrosoftPSF') has no launchers directly - # resolve it to its newest non-debug release subfolder. if ($null -eq $selected) { $selected = $discovered | Where-Object { $_.Name -like ($Framework + '\*') -and $_.Name -notlike '*debug*' } | Sort-Object Name -Descending | Select-Object -First 1 if ($null -ne $selected) { Write-Verbose "Resolved '$Framework' to latest release '$($selected.Name)'." } } if ($null -eq $selected) { $availableNames = $discovered | Select-Object -ExpandProperty Name Write-Error ("PSF '$Framework' not found. Available options:`n" + ($availableNames | ForEach-Object { " $_" } | Out-String).TrimEnd() + "`nUse Set-MSIXActivePSFFramework -List for details.") return } $Script:PSFVersion = $selected.Name $Script:PsfBasePath = $selected.Path Write-Host "Active PSF : $($selected.Name)" -ForegroundColor Green Write-Host "Path : $($Script:PsfBasePath)" -ForegroundColor Green # Warn if expected structure is incomplete $missingWarnings = @() if (-not (Test-Path (Join-Path $Script:PsfBasePath 'amd64'))) { $missingWarnings += "amd64\ subfolder (needed for x64 VCRuntime files)" } if (-not (Test-Path (Join-Path $Script:PsfBasePath 'win32'))) { $missingWarnings += "win32\ subfolder (needed for x86 VCRuntime files)" } foreach ($w in $missingWarnings) { Write-Warning "PSF installation is missing: $w" } } |