Support/ExecutionEngine/Eigenverft.Manifested.Sandbox.ExecutionEngine.Registry.ps1

<#
    Eigenverft.Manifested.Sandbox.ExecutionEngine.Registry
#>


function Test-RegistryPathExists {
<#
.SYNOPSIS
Returns whether a registry path exists.
 
.DESCRIPTION
Uses the PowerShell registry provider to test for the existence of a registry
path. Failures are treated as non-existent so callers can decide how to handle
missing or unreadable paths.
 
.EXAMPLE
Test-RegistryPathExists -Path 'HKLM:\SOFTWARE\Vendor\Product'
#>

    [CmdletBinding()]
    [OutputType([bool])]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    try {
        return (Test-Path -LiteralPath $Path)
    }
    catch {
        return $false
    }
}

function Get-RegistryValueData {
<#
.SYNOPSIS
Reads a value from a registry path.
 
.DESCRIPTION
Returns the read result in a normalized object so callers can distinguish
between missing paths, read failures, and successful value reads without
embedding registry-provider mechanics in higher-level workflows.
 
.EXAMPLE
Get-RegistryValueData -Path 'HKLM:\SOFTWARE\Vendor\Product' -ValueName 'Version'
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path,

        [AllowNull()]
        [string]$ValueName
    )

    if (-not (Test-RegistryPathExists -Path $Path)) {
        return [pscustomobject]@{
            Path          = $Path
            ValueName     = $ValueName
            Value         = $null
            Exists        = $false
            ReadSucceeded = $false
            Status        = 'Missing'
        }
    }

    if ([string]::IsNullOrWhiteSpace($ValueName)) {
        return [pscustomobject]@{
            Path          = $Path
            ValueName     = $null
            Value         = $null
            Exists        = $true
            ReadSucceeded = $true
            Status        = 'Ready'
        }
    }

    try {
        $properties = Get-ItemProperty -LiteralPath $Path -Name $ValueName -ErrorAction Stop
        return [pscustomobject]@{
            Path          = $Path
            ValueName     = $ValueName
            Value         = $properties.$ValueName
            Exists        = $true
            ReadSucceeded = $true
            Status        = 'Ready'
        }
    }
    catch {
        return [pscustomobject]@{
            Path          = $Path
            ValueName     = $ValueName
            Value         = $null
            Exists        = $true
            ReadSucceeded = $false
            Status        = 'Failed'
        }
    }
}

function Resolve-RegistryValueFromPaths {
<#
.SYNOPSIS
Finds the first usable registry path from a candidate list.
 
.DESCRIPTION
Evaluates registry candidate paths in order and returns the first successful
match. If no candidate succeeds, the last failed candidate is preserved so
callers can surface a meaningful path in diagnostics.
 
.EXAMPLE
Resolve-RegistryValueFromPaths -Paths @('HKLM:\A', 'HKLM:\B') -ValueName 'Version'
#>

    [CmdletBinding()]
    param(
        [AllowEmptyCollection()]
        [string[]]$Paths,

        [AllowNull()]
        [string]$ValueName
    )

    $candidatePaths = @($Paths | Where-Object { -not [string]::IsNullOrWhiteSpace([string]$_) })
    $result = [pscustomobject]@{
        Path         = if ($candidatePaths.Count -gt 0) { $candidatePaths[0] } else { $null }
        Paths        = @($candidatePaths)
        ValueName    = $ValueName
        ActualValue  = $null
        Status       = 'Missing'
    }

    foreach ($candidatePath in $candidatePaths) {
        $candidateResult = Get-RegistryValueData -Path $candidatePath -ValueName $ValueName
        if ($candidateResult.Status -eq 'Missing') {
            continue
        }

        $result.Path = $candidateResult.Path
        $result.ActualValue = $candidateResult.Value
        $result.Status = $candidateResult.Status

        if ($candidateResult.Status -eq 'Ready') {
            break
        }
    }

    return $result
}

function Get-WindowsUninstallRegistryEntry {
<#
.SYNOPSIS
Reads one Windows uninstall registry entry.
 
.DESCRIPTION
Returns a normalized uninstall-entry object from a concrete registry path. This
helper intentionally reads a direct key only; scanning uninstall roots belongs
in a separate helper when a package needs that behavior.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    $result = [ordered]@{
        Path                 = $Path
        Exists               = $false
        ReadSucceeded        = $false
        Status               = 'Missing'
        DisplayName          = $null
        DisplayVersion       = $null
        Publisher            = $null
        InstallLocation      = $null
        DisplayIcon          = $null
        UninstallString      = $null
        QuietUninstallString = $null
    }

    if (-not (Test-RegistryPathExists -Path $Path)) {
        return [pscustomobject]$result
    }

    $result.Exists = $true

    try {
        $properties = Get-ItemProperty -LiteralPath $Path -ErrorAction Stop
        $result.ReadSucceeded = $true
        $result.Status = 'Ready'
        foreach ($propertyName in @('DisplayName', 'DisplayVersion', 'Publisher', 'InstallLocation', 'DisplayIcon', 'UninstallString', 'QuietUninstallString')) {
            if ($properties.PSObject.Properties[$propertyName]) {
                $result[$propertyName] = $properties.$propertyName
            }
        }
    }
    catch {
        $result.Status = 'Failed'
    }

    return [pscustomobject]$result
}

function Get-WindowsRegistryExecutablePathFromText {
    [CmdletBinding()]
    param(
        [AllowNull()]
        [string]$Text
    )

    if ([string]::IsNullOrWhiteSpace($Text)) {
        return $null
    }

    $candidateText = [Environment]::ExpandEnvironmentVariables(([string]$Text).Trim())
    if ($candidateText.StartsWith('"')) {
        $quotedMatch = [regex]::Match($candidateText, '^"([^"]+)"')
        if ($quotedMatch.Success) {
            return $quotedMatch.Groups[1].Value
        }
    }

    $extensionMatch = [regex]::Match($candidateText, '^(.*?\.(?:exe|msi|cmd|bat))(?=\s|,|$)', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
    if ($extensionMatch.Success) {
        return ($extensionMatch.Groups[1].Value -replace ',\s*\d+$', '').Trim()
    }

    $firstToken = ($candidateText -split '\s+', 2)[0]
    return ($firstToken -replace ',\s*\d+$', '').Trim()
}

function Resolve-WindowsUninstallRegistryEntryPath {
<#
.SYNOPSIS
Resolves a filesystem path from a Windows uninstall registry entry.
 
.DESCRIPTION
Extracts common install-related paths from a normalized uninstall entry. The
helper accepts explicit source names so package logic can stay declarative and
future installer packages can add path-source strategies without changing
existing discovery flow.
#>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [psobject]$Entry,

        [Parameter(Mandatory = $true)]
        [ValidateSet('installLocation', 'displayIcon', 'displayIconDirectory', 'uninstallString', 'uninstallStringDirectory')]
        [string]$Source
    )

    $rawPath = $null
    switch -Exact ($Source) {
        'installLocation' {
            $rawPath = if ($Entry.PSObject.Properties['InstallLocation']) { [string]$Entry.InstallLocation } else { $null }
        }
        'displayIcon' {
            $rawPath = Get-WindowsRegistryExecutablePathFromText -Text $(if ($Entry.PSObject.Properties['DisplayIcon']) { [string]$Entry.DisplayIcon } else { $null })
        }
        'displayIconDirectory' {
            $iconPath = Get-WindowsRegistryExecutablePathFromText -Text $(if ($Entry.PSObject.Properties['DisplayIcon']) { [string]$Entry.DisplayIcon } else { $null })
            $rawPath = if ([string]::IsNullOrWhiteSpace($iconPath)) { $null } else { Split-Path -Parent $iconPath }
        }
        'uninstallString' {
            $rawPath = Get-WindowsRegistryExecutablePathFromText -Text $(if ($Entry.PSObject.Properties['UninstallString']) { [string]$Entry.UninstallString } else { $null })
        }
        'uninstallStringDirectory' {
            $uninstallPath = Get-WindowsRegistryExecutablePathFromText -Text $(if ($Entry.PSObject.Properties['UninstallString']) { [string]$Entry.UninstallString } else { $null })
            $rawPath = if ([string]::IsNullOrWhiteSpace($uninstallPath)) { $null } else { Split-Path -Parent $uninstallPath }
        }
    }

    $result = [ordered]@{
        EntryPath    = if ($Entry.PSObject.Properties['Path']) { $Entry.Path } else { $null }
        Source       = $Source
        RawPath      = $rawPath
        ResolvedPath = $null
        Status       = 'Missing'
    }

    if ([string]::IsNullOrWhiteSpace($rawPath)) {
        return [pscustomobject]$result
    }

    try {
        $expandedPath = [Environment]::ExpandEnvironmentVariables([string]$rawPath)
        $result.ResolvedPath = [System.IO.Path]::GetFullPath($expandedPath)
        $result.Status = 'Ready'
    }
    catch {
        $result.ResolvedPath = $rawPath
        $result.Status = 'Failed'
    }

    return [pscustomobject]$result
}