Public/Get-InstalledSoftware.ps1

using namespace System.Collections.Generic
using namespace System.Management.Automation
using namespace System.Security.AccessControl
using namespace Microsoft.Win32

function Get-InstalledSoftware {
  <#
      .NOTES
      https://gist.github.com/SeeminglyScience/d7be8c59875bd389df820c8356f137f9
  #>

  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline)]
    [ValidateNotNullOrEmpty()]
    [SupportsWildcards()]
    [string] $Name
  )
  begin {
    $AllNames = $null
  }
  process {
    if (-not $PSBoundParameters.ContainsKey('Name') -or [string]::IsNullOrEmpty($Name)) {
      return
    }

    if ($null -eq $AllNames) {
      $StartingCapacity = 1
      if ($MyInvocation.ExpectingInput) {
        $StartingCapacity = 4
      }
      $AllNames = [List[string]]::new($StartingCapacity)
    }
    $AllNames.Add($Name)
  }
  end {
    if ($null -ne $AllNames) {
      $Wildcards = [WildcardPattern[]]::new($AllNames.Count)
      for ($I = 0; $I -lt $AllNames.Count; $I++) {
        $Wildcards[$I] = [WildcardPattern]::new(
          $AllNames[$I],
          [WildcardOptions]::IgnoreCase
        )
      }
    }

    # Don't use the registry provider for performance and to allow us to open the
    # 64 bit registry view from a 32 bit process.
    $HKLM = $null
    $HKCU = $null

    $OwnsKeyHKLM = $false
    $OwnsKeyHKCU = $false
    try {
      $RegistryPaths = (
        @{
          Path = 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
          Is64Bit = $true
        },
        @{
          Path = 'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
          Is64Bit = $false
      })

      $HKLM = [Registry]::LocalMachine
      $HKCU = [Registry]::CurrentUser

      if (-not [Environment]::Is64BitProcess) {
        if ([Environment]::Is64BitOperatingSystem) {
          $OwnsKeyHKLM = $true
          $HKLM = [RegistryKey]::OpenBaseKey([RegistryHive]::LocalMachine,[RegistryView]::Registry64)

          $OwnsKeyHKCU = $true
          $HKCU = [RegistryKey]::OpenBaseKey([RegistryHive]::CurrentUser,[RegistryView]::Registry64)
        }
        else {
          $RegistryPaths = @($RegistryPaths[0])
          $RegistryPaths[0].Is64Bit = $false
        }
      }


      foreach($RegHive in @($HKLM,$HKCU)) {

        foreach ($RegistryPath in $RegistryPaths) {
          $Software = $null
          try {
            # $Software = $HKLM.OpenSubKey($RegistryPath.Path)

            if (Join-Path -Path ('registry::{0}' -f $RegHive.Name) -ChildPath $RegistryPath.Path -Resolve -ErrorAction SilentlyContinue) {

              $Software = $RegHive.OpenSubKey($RegistryPath.Path)
              foreach ($SubKeyName in $Software.GetSubKeyNames()) {
                $SubKey = $null
                try {
                  $SubKey = $Software.OpenSubKey($SubKeyName,[RegistryRights]::QueryValues)

                  $DisplayName = $SubKey.GetValue('DisplayName')
                  if ([string]::IsNullOrEmpty($DisplayName)) {
                    continue
                  }

                  if ($Wildcards.Length -gt 0) {
                    $WasMatchFound = $false
                    foreach ($Wildcard in $Wildcards) {
                      if ($Wildcard.IsMatch($DisplayName)) {
                        $WasMatchFound = $true
                        break
                      }
                    }

                    if (-not $WasMatchFound) {
                      continue
                    }
                  }

                  $InstalledOn = $SubKey.GetValue('InstallDate')
                  if (-not [string]::IsNullOrWhiteSpace($InstalledOn)) {
                    if ($InstalledOn -as [datetime]) {
                      $InstalledOn = [datetime]$InstalledOn
                    }
                    else {
                      $InstalledOn = [datetime]::ParseExact($InstalledOn, 'yyyyMMdd', $null)
                    }
                  }

                  # yield
                  [PSCustomObject]@{
                    PSTypeName = 'Utility.InstalledSoftware'
                    Name = $DisplayName
                    Publisher = $SubKey.GetValue('Publisher')
                    DisplayVersion = $SubKey.GetValue('DisplayVersion')
                    Uninstall = $SubKey.GetValue('UninstallString')
                    Guid = $SubKeyName
                    InstallDate = $InstalledOn
                    Is64Bit = $RegistryPath.Is64Bit
                    PSPath = 'Microsoft.PowerShell.Core\Registry::{2}\{0}\{1}' -f (
                      $RegistryPath.Path,
                      $SubKeyName,
                      $RegHive.Name
                    )
                  }
                }
                catch {
                  $PSCmdlet.WriteError($PSItem)
                }
                finally {
                  if ($null -ne $SubKey) {
                    $SubKey.Dispose()
                  }
                }
              }
            }

          }
          finally {
            if ($null -ne $Software) {
              $Software.Dispose()
            }
            $DisplayName,$InstalledOn = $null
          }
        }
      }

    }
    finally {
      if ($OwnsKeyHKLM -and $null -ne $HKLM) {
        $HKLM.Dispose()
      }
      if ($OwnsKeyHKCU -and $null -ne $HKCU) {
        $HKCU.Dispose()
      }
    }
  }
}