How to use [Microsoft.Win32.RegistryValueKind]::TryParse

Viewed 92

I can't seem to get [Microsoft.Win32.RegistryValueKind]::TryParse to work properly. Each solution I try always returns Cannot find an overload for "TryParse" and the argument count: "2". (or "3") despite the fact it shows the following.

PS C:\Windows\system32> [Microsoft.Win32.RegistryValueKind]::TryParse

OverloadDefinitions                                                                                                                          
-------------------                                                                                                                          
static bool TryParse[TEnum](string value, [ref] TEnum result)                                                                                
static bool TryParse[TEnum](string value, bool ignoreCase, [ref] TEnum result)

In my experience any of the following should normally work just fine.

[Microsoft.Win32.RegistryValueKind]::TryParse('dword',[ref]$Null)
[Microsoft.Win32.RegistryValueKind]::TryParse('dword',$true,[ref]$Null)

In the meantime I can just cast the string as [Microsoft.Win32.RegistryValueKind] but I am curious as to what I am doing wrong.

3 Answers

TryParse is actually implemented by System.Enum, and is a generic method. It returns a bool whether the parse succeeded, and the second parameter is an out with the actual parse result.

You have a number of issues if you want to use TryParse.

Firstly, you cannot call a generic method directly in older versions of Powershell. If you are using PS 7.3 or above then you can now do so:

[System.Enum]::TryParse[Microsoft.Win32.RegistryValueKind]('DWord', [ref]$prm)

For versions of Powershell which use .Net Core or .Net 5 (I don't if there are any), you could also use the non-generic version of TryParse

[System.Enum]::TryParse([Microsoft.Win32.RegistryValueKind], 'DWord', [ref]$prm)

Be that as it may, in earlier versions you need to use reflection.

$arrParams =  @('DWord', $null)

$method = ([System.Enum].GetMethods() | where {($_.Name -eq "TryParse") -and ($_.GetParameters().Length -eq 2) })[0].MakeGenericMethod(@([Microsoft.Win32.RegistryValueKind]))

$method.Invoke($null, $arrParams);

$arrParams[1]

Note that the string parameter is case-sensitive, and therefore must be 'DWord'

The [Microsoft.Win32.RegistryValueKind]::TryParse() method by itself will return either $true or $false, depending if the parsing succeeded or not.

  • The first parameter is the string value you want parsed converted to one of the RegistryValueKind enum types.
  • The last parameter is the variable containing the RegistryValueKind type of choice.
    The method then tries to convert the given string value to that registry kind.
  • Optionally, there is an overload with a second parameter in between you can set to $true to have it work case-insensitive on the given string value.

example:

[Microsoft.Win32.RegistryValueKind]::TryParse('12345', [ref][Microsoft.Win32.RegistryValueKind]::DWord)

returns True because '12345' can be converted to integer DWord valuekind.

You can also read back the parsed value like this

$result = [Microsoft.Win32.RegistryValueKind]::DWord   # 4
if ([Microsoft.Win32.RegistryValueKind]::TryParse('12345', [ref]$result)) {
    Write-Host "The value can be stored as $result (DWord)"
}

Anyway, if your intention is to read registry values and try to determine if the RegistryKind of that entry is correct, I think the RegistryKey.GetValueKind() method would be easier to use

To complement Charlieface's helpful answer:

  • It is mentioned in that answer, but let me highlight it: in PowerShell (Core) 7.3+[1], calls to generic methods are now fully supported; unlike in C#, where type arguments are specified enclosed in <...>, PowerShell requires the usual [...] around type names, as shown below.

  • System.Enum.TryParse is case-sensitive by default, but case-insensitive lookup is available by opt-in, via the ignoreCase parameter.

Case-insensitive calls:

# PowerShell (Core)
[enum]::TryParse([Microsoft.Win32.RegistryValueKind], 'dword', $true, [ref] $null)

# PowerShell (Core) 7.3+ alternative: invocation as a *generic method*.
# Note:
#  * The type name is part of the method name, before the opening "(" 
#  * 0 rather than $null is passed, because 0 can be converted to
#    [Microsoft.Win32.RegistryValueKind], which is necessary for matching 
#    the generic method's signature.
[enum]::TryParse[Microsoft.Win32.RegistryValueKind]('dword', $true, [ref] 0)

# Windows PowerShell: reflection is needed.
# Note: In this case, $null must be passed without [ref].
[enum].GetMethods('Public, Static').
  Where({ $_.IsGenericMethod -and $_.GetParameters().Count -eq 3 }).
  MakeGenericMethod([Microsoft.Win32.RegistryValueKind]).
  Invoke($null, ('dword', $true, $null))

As for PowerShell-idiomatic solutions, which are case-insensitive by default (and invariably, in this case), as PowerShell generally is:

# Note: 
#  * The assumption is that no strict mode or at most Set-StrictMode -Version 1
#    is in effect.
#  * '...' isn't strictly necessary here; you may also use a *variable*.

[Microsoft.Win32.RegistryValueKind]::'dword' -is [enum] # -> $true

It's also worth noting that working with string representations of enumeration values is usually sufficient in PowerShell, as PowerShell automatically converts them to enum types.

You can also use a cast to the target type:

# -> [Microsoft.Win32.RegistryValueKind]::DWord
[Microsoft.Win32.RegistryValueKind] 'dword'

Since such a cast or an implicit conversion fails, another alternative to ::TryParse() is (this one doesn't depend on the active strict mode).

try { $null = [Microsoft.Win32.RegistryValueKind] 'dword'; $true } catch { $false }

[1] 7.3 is yet to be released as of this writing, but already available in preview versions - see the repo

Related