Private/Get-EFInstalledApplicationEvidence.ps1

function Get-EFInstalledApplicationEvidence {
    [CmdletBinding()]
    param(
        [ValidateSet('Machine', 'CurrentUser', 'All')]
        [string]$Scope = 'Machine',

        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$ApplicationName,

        [string]$Publisher,

        [string]$ProductCode,

        [ValidateSet('All', 'x64', 'x86', 'User')]
        [string]$Architecture = 'All',

        [ValidateRange(100, 10000)]
        [int]$MaximumRecords = 5000
    )

    $locations = @()
    if ($Scope -in @('Machine', 'All')) {
        if ([Environment]::Is64BitOperatingSystem) {
            $locations += [pscustomobject]@{
                Hive = [Microsoft.Win32.RegistryHive]::LocalMachine
                View = [Microsoft.Win32.RegistryView]::Registry64
                Scope = 'Machine'
                Architecture = 'x64'
            }
        }
        $locations += [pscustomobject]@{
            Hive = [Microsoft.Win32.RegistryHive]::LocalMachine
            View = [Microsoft.Win32.RegistryView]::Registry32
            Scope = 'Machine'
            Architecture = 'x86'
        }
    }
    if ($Scope -in @('CurrentUser', 'All')) {
        $locations += [pscustomobject]@{
            Hive = [Microsoft.Win32.RegistryHive]::CurrentUser
            View = [Microsoft.Win32.RegistryView]::Default
            Scope = 'CurrentUser'
            Architecture = 'User'
        }
    }

    $entries = [Collections.Generic.List[object]]::new()
    $errors = [Collections.Generic.List[string]]::new()
    $recordsScanned = 0
    $limitReached = $false
    $uninstallPath = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
    $valueOptions = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
    foreach ($location in $locations) {
        if ($Architecture -ne 'All' -and $location.Architecture -ne $Architecture) { continue }
        $baseKey = $null
        $uninstallKey = $null
        try {
            $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey($location.Hive, $location.View)
            $uninstallKey = $baseKey.OpenSubKey($uninstallPath, $false)
            if ($null -eq $uninstallKey) { continue }
            $remainingRecordBudget = $MaximumRecords - $recordsScanned
            if ($uninstallKey.SubKeyCount -gt $remainingRecordBudget) {
                $errors.Add("The installed-application scan exceeded its $MaximumRecords-record safety limit.")
                $limitReached = $true
                continue
            }

            foreach ($subKeyName in @($uninstallKey.GetSubKeyNames())) {
                $recordsScanned++
                if ($recordsScanned -gt $MaximumRecords) {
                    $errors.Add("The installed-application scan exceeded its $MaximumRecords-record safety limit.")
                    $limitReached = $true
                    break
                }
                $entryKey = $null
                try {
                    $entryKey = $uninstallKey.OpenSubKey($subKeyName, $false)
                    if ($null -eq $entryKey) {
                        $errors.Add("$($location.Scope) $($location.Architecture): one uninstall record could not be opened.")
                        continue
                    }
                    $displayName = [string]$entryKey.GetValue('DisplayName', $null, $valueOptions)
                    if ([string]::IsNullOrWhiteSpace($displayName)) { continue }
                    if (-not [string]::Equals($displayName, $ApplicationName, [StringComparison]::OrdinalIgnoreCase)) { continue }
                    $entryPublisher = [string]$entryKey.GetValue('Publisher', $null, $valueOptions)
                    if (-not [string]::IsNullOrWhiteSpace($Publisher) -and
                        -not [string]::Equals($entryPublisher, $Publisher, [StringComparison]::OrdinalIgnoreCase)) { continue }
                    if (-not [string]::IsNullOrWhiteSpace($ProductCode) -and
                        -not [string]::Equals([string]$subKeyName, $ProductCode, [StringComparison]::OrdinalIgnoreCase)) { continue }

                    $entries.Add([pscustomobject]@{
                        Name         = $displayName
                        Version      = [string]$entryKey.GetValue('DisplayVersion', $null, $valueOptions)
                        Publisher    = $entryPublisher
                        ProductCode  = [string]$subKeyName
                        Scope        = $location.Scope
                        Architecture = $location.Architecture
                    })
                }
                catch {
                    $errors.Add("$($location.Scope) $($location.Architecture): one uninstall record could not be read.")
                }
                finally {
                    if ($null -ne $entryKey) { $entryKey.Dispose() }
                }
            }
        }
        catch {
            $errors.Add("$($location.Scope) $($location.Architecture): the uninstall registry view could not be read.")
        }
        finally {
            if ($null -ne $uninstallKey) { $uninstallKey.Dispose() }
            if ($null -ne $baseKey) { $baseKey.Dispose() }
        }
        if ($limitReached) { break }
    }

    [pscustomobject]@{
        Entries     = @($entries)
        DataStatus  = if ($errors.Count -eq 0) { 'Complete' } else { 'Partial' }
        ErrorCount  = $errors.Count
        Errors      = @($errors)
        RecordsScanned = $recordsScanned
    }
}