Reputation: 6883
Given a variable $property
and a variable $path
this code will populate the $property
variable appropriately.
$path = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Source\Key\PropertyInKey'
$property = Get-ItemProperty -path:(Split-Path $path -parent) -name:(Split-Path $path -leaf)
Now I want to be able to pass the $property
variable and extract the kind
and value
of that property. Working from the path rather than the property object I can do that with
$key = Get-Item (Split-Path $path -parent)
$value =$key.GetValue((Split-Path $path -leaf))
$kind = $key.GetValueKind((Split-Path $path -leaf))
But the $property
variable is an odd [PSCustomObject]
where kind
and value
are not directly accessible. And the named properties that I can directly access are really about the parent key. PSPath
is actually the parent key, and PSParentPath
& PSChildName
also really applies to the parent key as well.
PropertyInKey : SomeTextHere
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Source\Key
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Source
PSChildName : Key
PSProvider : Microsoft.PowerShell.Core\Registry
I can use PSPath
with Get-Item
as shown to get that key, but to get the kind and value data I need to know the property name. My first thought was to filter properties and assume the one that doesn't start with PS
is the Property Name. But that doesn't work because vendors who write to the registry aren't going to respect this PowerShell prefix. Indeed, some registry property names could predate the existence of PowerShell.
So then I thought I could create a list of the four PS
properties of the object that I don't want, and the one remaining property is the one I do want.
At least, I assume there are no other hidden properties I also have to filter.
So, wondering here if there IS an elegant way to extract the property NAME from this semi-useless object so I can get the data I want, or am I forced to actually path the path so I can handle it with Split-Path?
The code that will be calling this function uses Get-Item
to then pass files, folders and keys, so being able to Get-ItemProperty and pass that would be consistent. But since Get-ItemProperty lies, and doesn't get a property, it gets partial data and bundles it into a PS custom object, I am banging my head a bit.
Why @%$#$ Microsoft doesn't at least output Name, Value and Kind properties here I have no clue. And then, why a custom object, rather than actually having a real Property object from the registry, the way a Key is a [Microsoft.Win32.RegistryKey]
?
EDIT: Based on looking at hidden stuff with
$property | Get-Member -Force | Format-Table
which produces
Name MemberType Definition
---- ---------- ----------
pstypenames CodeProperty System.Collections.ObjectModel.Collection`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] pstypenames{get=PSTypeNames;}
psadapted MemberSet psadapted {PropertyInKey, PSPath, PSParentPath, PSChildName, PSProvider, ToString, Equals, GetHashCode, GetType}
psbase MemberSet psbase {BaseObject, Members, Properties, Methods, ImmediateBaseObject, TypeNames, get_BaseObject, get_Members, get_Properties, get_Methods, get_ImmediateBaseObject, ToString, Co...
psextended MemberSet psextended {PropertyInKey, PSPath, PSParentPath, PSChildName, PSProvider}
psobject MemberSet psobject {BaseObject, Members, Properties, Methods, ImmediateBaseObject, TypeNames, get_BaseObject, get_Members, get_Properties, get_Methods, get_ImmediateBaseObject, ToString, ...
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
PropertyInKey NoteProperty string PropertyInKey=SomeTextHere
PSChildName NoteProperty string PSChildName=Key
PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\PxTools_Copy\Source
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\PxTools_Copy\Source\Key
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Registry
It seems like I could filter on MemberType
is NoteProperty
and then filter again on Name
is not in @(PSChildName, PSParentPath, PSPath, PSProvider)
and end up with what I am looking for. But HOW to do that filtering is making my Brian hurt.
EDIT2: Well, here is what I have, and it works, but good lord, what a mess, just to get some data that Microsoft should have made easily available.
($property | Get-Member -MemberType Properties | Select-Object -Property Name | Where {$_.Name -notin @('PSChildName', 'PSParentPath', 'PSPath', 'PSProvider')}).Name
Upvotes: 2
Views: 1686
Reputation: 440431
You can streamline your own attempt to get all of a registry key's values (properties) by value name (property name) and value data (property value), excluding the properties that PowerShell itself adds, using the intrinsic psobject
property:
$keyPath = 'HKLM:\Software\Classes\htmlfile' # sample path
(Get-ItemProperty -LiteralPath $keyPath).psobject.Properties |
Where Name -notin PSChildName, PSPath, PSProvider, PSDrive, PSParentPath |
Select Name, Value
For a given, single value (property):
$keyAndValuePath = 'HKLM:\Software\Classes\htmlfile\EditFlags'
(Get-ItemProperty -LiteralPath (Split-Path $keyAndValuePath)).
psobject.Properties[(Split-Path -Leaf $keyAndValuePath)] |
Select Name, Value
Note: This is somewhat inefficient, because it involves the retrieval of all values; you could add -Name (Split-Path -Leaf $keyAndValuePath)
to the Get-ItemProperty
call to avoid that, which has been omitted here for brevity and convenience.
However, in order to also obtain the values' registry kind information, however, which you have to glean from the [Microsoft.Win32.RegistryKey]
instance that Get-Item
- not Get-ItemProperty
- returns:
For all values (properties) of a given registry key:
$keyPath = 'HKLM:\Software\Classes\htmlfile'
Get-Item -LiteralPath $keyPath |
ForEach-Object {
foreach ($valueName in $_.GetValueNames()) {
[pscustomobject] @{
Name = $valueName
Value = $_.GetValue($valueName)
Kind = $_.GetValueKind($valueName)
}
}
}
For a given, single value (property):
$keyAndValuePath = 'HKLM:\Software\Classes\htmlfile\EditFlags'
Get-Item -LiteralPath (Split-Path $keyAndValuePath) |
ForEach-Object {
$valueName = Split-Path -Leaf $keyAndValuePath
[pscustomobject] @{
Name = $valueName
Value = $_.GetValue($valueName)
Kind = $_.GetValueKind($valueName)
}
}
The .NET registry API, [Microsoft.Win32.RegistryKey]
, has no object model for what is called a registry value, which can be conceived of as a key's property with a name, value (data, in registry terms), and a kind (a registry-specific data type), which is mapped onto a .NET type. That is, you can query the list of value names (.GetValueNames()
), and you can ask for a specific value's information (e.g. GetValueKind($name)
), but a registry value entity as a whole has no object (.NET type) representation.
PowerShell's registry provider superimposes an incomplete, awkward object model in the context of its Get-ItemProperty
cmdlet:
Instead of a collection of objects each representing a registry value, it returns a single object, whose properties each represent a registry value, incompletely (given that a property has just a name and a value, so that additional information such as the kind cannot be represented).
Because the property names of the resulting objects - representing the value names - can by definition differ from key to key, no single .NET type can be used, and PowerShell's ad-hoc "property-bag" [pscustomobject]
instances are used instead.
PowerShell additionally decorates the instances with PS*
ETS (Extended Type System) provider properties reflecting information about the parent key, such as PSPath
.
The upshot is:
You must filter out those ETS properties in order to get only those properties that truly represent registry values, as shown in the top section.
There is at least a hypothetical chance of naming conflicts.
To get additional information about a registry value, notably its kind, you need to resort to the underlying .NET API directly, as shown in the middle section, which is in essence an attempt to establish a more useful, custom object model.
Upvotes: 4