Test if registry value exists

In my powershell script I'm creating one registry entry for each element I run script on and I would like to store some additional info about each element in registry (if you specify optional parameters once then by default use those params in the future).

The problem I've encountered is that I need to perform Test-RegistryValue (like here--see comment) but it doesn't seem to do the trick (it returns false even if entry exists). I tried to "build on top of it" and only thing I came up is this:

Function Test-RegistryValue($regkey, $name) 
{
    try
    {
        $exists = Get-ItemProperty $regkey $name -ErrorAction SilentlyContinue
        Write-Host "Test-RegistryValue: $exists"
        if (($exists -eq $null) -or ($exists.Length -eq 0))
        {
            return $false
        }
        else
        {
            return $true
        }
    }
    catch
    {
        return $false
    }
}

That unfortunately also doesn't do what I need as it seems it always selects some (first?) value from the registry key.

Anyone has idea how to do this? It just seems too much to write managed code for this...


Solution 1:

Personally, I do not like test functions having a chance of spitting out errors, so here is what I would do. This function also doubles as a filter that you can use to filter a list of registry keys to only keep the ones that have a certain key.

Function Test-RegistryValue {
    param(
        [Alias("PSPath")]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [String]$Path
        ,
        [Parameter(Position = 1, Mandatory = $true)]
        [String]$Name
        ,
        [Switch]$PassThru
    ) 

    process {
        if (Test-Path $Path) {
            $Key = Get-Item -LiteralPath $Path
            if ($Key.GetValue($Name, $null) -ne $null) {
                if ($PassThru) {
                    Get-ItemProperty $Path $Name
                } else {
                    $true
                }
            } else {
                $false
            }
        } else {
            $false
        }
    }
}

Solution 2:

The Carbon PowerShell module has a Test-RegistryKeyValue function that will do this check for you. (Disclosure: I am the owner/maintainer of Carbon.)

You have to check that that the registry key exists, first. You then have to handle if the registry key has no values. Most of the examples here are actually testing the value itself, instead of the existence of the value. This will return false negatives if a value is empty or null. Instead, you have to test if a property for the value actually exists on the object returned by Get-ItemProperty.

Here's the code, as it stands today, from the Carbon module:

function Test-RegistryKeyValue
{
    <#
    .SYNOPSIS
    Tests if a registry value exists.

    .DESCRIPTION
    The usual ways for checking if a registry value exists don't handle when a value simply has an empty or null value.  This function actually checks if a key has a value with a given name.

    .EXAMPLE
    Test-RegistryKeyValue -Path 'hklm:\Software\Carbon\Test' -Name 'Title'

    Returns `True` if `hklm:\Software\Carbon\Test` contains a value named 'Title'.  `False` otherwise.
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        # The path to the registry key where the value should be set.  Will be created if it doesn't exist.
        $Path,

        [Parameter(Mandatory=$true)]
        [string]
        # The name of the value being set.
        $Name
    )

    if( -not (Test-Path -Path $Path -PathType Container) )
    {
        return $false
    }

    $properties = Get-ItemProperty -Path $Path 
    if( -not $properties )
    {
        return $false
    }

    $member = Get-Member -InputObject $properties -Name $Name
    if( $member )
    {
        return $true
    }
    else
    {
        return $false
    }

}

Solution 3:

The best way to test if a registry value exists is to do just that - test for its existence. This is a one-liner, even if it's a little hard to read.

(Get-ItemProperty $regkey).PSObject.Properties.Name -contains $name

If you actually look up its data, then you run into the complication of how Powershell interprets 0.

Solution 4:

I would go with the function Get-RegistryValue. In fact it gets requested values (so that it can be used not only for testing). As far as registry values cannot be null, we can use null result as a sign of a missing value. The pure test function Test-RegistryValue is also provided.

# This function just gets $true or $false
function Test-RegistryValue($path, $name)
{
    $key = Get-Item -LiteralPath $path -ErrorAction SilentlyContinue
    $key -and $null -ne $key.GetValue($name, $null)
}

# Gets the specified registry value or $null if it is missing
function Get-RegistryValue($path, $name)
{
    $key = Get-Item -LiteralPath $path -ErrorAction SilentlyContinue
    if ($key) {
        $key.GetValue($name, $null)
    }
}

# Test existing value
Test-RegistryValue HKCU:\Console FontFamily
$val = Get-RegistryValue HKCU:\Console FontFamily
if ($val -eq $null) { 'missing value' } else { $val }

# Test missing value
Test-RegistryValue HKCU:\Console missing
$val = Get-RegistryValue HKCU:\Console missing
if ($val -eq $null) { 'missing value' } else { $val }

OUTPUT:

True
54
False
missing value