Summary: Richard Siddaway investigates how to use CIM to manage the registry.
Honorary Scripting Guy, Richard Siddaway, here filling in for my good friend The Scripting Guy. Today, I'm starting a series about registry cmdlets by investigating how to work with the registry.
The bad news is that there aren’t any cmdlets for working with the registry. There’s a Registry provider, which means you can use the Item and ItemProperty cmdlets to manage the local registry—but there aren’t any specific registry cmdlets associated with the provider.
The good news is that we can adopt the approach that many teams at Microsoft have taken and create our own by using cmdlet definition XML (CDXML). A Common Information Model (CIM) class is wrapped in some fairly simple XML and published as a Windows PowerShell module. If you look in the Modules folder on a computer running Windows 8.1, Windows 8, Windows Server 2012 R2, or Windows Server 2012, you will find many files with a CDXML extension:
Get-ChildItem -Path C:\Windows\System32\WindowsPowerShell\v1.0\Modules -Filter *.CDXML -Recurse
These files are a great resource for figuring out how CDXML should be constructed. Other useful resources include:
- Using cmdlet definition XML to create Windows PowerShell cmdlets (whitepaper)
- Cmdlet definition XML (useful examples on MSDN)
By the end of this series, you will have a Registry module that will make working with the registry much easier, and you will have learned how to use CDXML so that you can create other modules based on your favorite CIM classes.
First though, we should have a quick recap of using CIM to work with the registry.
Note Although there is a technical difference between CIM and WMI, I will be using them interchangeably, which seems to be common practice.
The registry is accessed through the StdRegProv class. You can examine the class like this:
Get-CimClass -Namespace root\cimv2 -ClassName StdRegProv
NameSpace: ROOT/cimv2
CimClassName CimClassMethods CimClassProperties
------------ --------------- ------------------
StdRegProv {CreateKey, Delet... {}
The class is also available in the root\default namespace. This is the only option if you are running Windows XP or Windows Server 2003. These versions are no longer supported, so we’ll use the root\cimv2 namespace for the remainder of the series. If you want to check if the classes are the same, you can examine the root\default version like this:
Get-CimClass -Namespace root\default -ClassName StdRegProv
When you look at the StdRegProv class, the first thing you notice is that it has no properties. The class provides methods only. To drill into the methods:
$class = Get-CimClass -Namespace root\cimv2 -ClassName StdRegProv
$class.CimClassMethods | select Name
Name
----
CreateKey
DeleteKey
EnumKey
EnumValues
DeleteValue
SetDWORDValue
SetQWORDValue
GetDWORDValue
GetQWORDValue
SetStringValue
GetStringValue
SetMultiStringValue
GetMultiStringValue
SetExpandedStringValue
GetExpandedStringValue
SetBinaryValue
GetBinaryValue
CheckAccess
SetSecurityDescriptor
GetSecurityDescriptor
As shown here, all the methods are static:
£> $class.CimClassMethods["GetSTRINGvalue"] | Format-List
Name : GetStringValue
ReturnType : UInt32
Parameters : {hDefKey, sSubKeyName, sValueName, sValue}
Qualifiers : {implemented, static}
This means that you don’t need an instance of the class to work with—you can simply use the methods. The parameters for these methods are relatively straightforward:
£> $class.CimClassMethods["GetSTRINGvalue"].Parameters
Name CimType Qualifiers
---- ------- ----------
hDefKey UInt32 {ID, IN}
sSubKeyName String {ID, IN}
sValueName String {ID, in}
sValue String {ID, out}
This is where we meet the first nasty bit. The hdefkey parameter defines the registry hive you want to work with. It’s an unsigned integer that can take the following values:
HKEY_CLASSES_ROOT = 2147483648 (0x80000000)
HKEY_CURRENT_USER = 2147483649 (0x80000001)
HKEY_LOCAL_MACHINE = 2147483650 (0x80000002)
HKEY_USERS = 2147483651 (0x80000003)
HKEY_CURRENT_CONFIG = 2147483653 (0x80000005)
HKEY_DYN_DATA = 2147483654 (0x80000006)
This list shows the hive and the related unsigned integer as a decimal and hexadecimal number. I have found that it’s easiest to define these values as variables. For instance, to work with the HKEY_LOCAL_MACHINE hive, use:
[uint32]$hklm = 2147483650
Let’s look at a little Windows PowerShell history for a moment...
When Windows PowerShell 1.0 launched, it immediately gave us a way to work with WMI (Get-WmiObject). That was a huge step forward from using VBScript. Unfortunately, we couldn’t use the StdRegProv class directly because of its static methods. The answer was to use the [wmiclass] type accelerator:
$reg = [wmiclass]"\\.\root\cimv2:StdRegprov"
Then you can use Get-Member like this:
$reg | Get-Member
Now you'll see an object from the System.Management.ManagementClass:
System.Management.ManagementClass#ROOT\cimv2\StdRegProv
I tend to use variables for things like registry keys and values because it makes my code reusable. A couple of relatively safe values to use are:
$subkey = "SOFTWARE\Microsoft\Internet Explorer"
$value = "Version"
You can then call a method on the class:
$reg.GetSTRINGvalue($hklm, $subkey, $value)
The important parts of the results are:
ReturnValue : 0
sValue : 9.11.9600.17126
ReturnValue is the return code. A value of 0 in the ReturnValue is good. Anything else is bad, and could be very bad. Finding what the return code means can be difficult, but some information is available if you search WMI return values.
The sValue property holds the return value you want. You can access it directly by using standard techniques:
$reg.GetSTRINGvalue($hklm, $subkey, $value) | select -ExpandProperty sValue
–or–
($reg.GetSTRINGvalue($hklm, $subkey, $value)).sValue
This technique is still valid and works with the latest versions of Windows PowerShell. However, working with the registry became much easier in Windows PowerShell 2.0 when Invoke-WmiMethod joined the game. Our registry access code became:
Invoke-WmiMethod -Namespace root\cimv2 -Class StdRegProv -Name GetSTRINGvalue -ArgumentList $hklm, $subkey, $value
However, life is never straightforward...a bit of a problem dropped into our laps. To explain, I need to create a registry key to work with. I can use the $reg object that I created earlier:
$newkey = "SOFTWARE\HSGtest"
$reg.CreateKey($hklm, $newkey)
And let’s add a value:
$newname = 'Date'
$newvalue = 'June 2013'
$reg.SetSTRINGvalue($hklm, $newkey, $newname, $newvalue)
And finally, let's test that it’s created correctly:
$reg.GetSTRINGvalue($hklm, $newkey, $newname)
The truncated output shows:
ReturnValue : 0
sValue : June 2013
Oops! This where I realize that I’ve used the wrong data, so now I need to modify the data:
$newvalue = 'June 2014'
And for a change, I’ll use Invoke-WmiMethod:
Invoke-WmiMethod -Namespace root\cimv2 -Class StdRegProv -Name SetSTRINGvalue $hklm, $newkey, $newname, $newvalue
A return code of 0 indicates that it worked. But let’s assume I’m a bit paranoid and I want to check the data:
$reg.GetSTRINGvalue($hklm, $newkey, $newname)
What I get is:
ReturnValue : 0
sValue : June 2013
In reality, the value hasn’t been changed. This isn’t a Windows PowerShell issue. It's related to the underlying .NET classes. It turns out that when you are using Invoke-WmiMethod, the order of the arguments matters. The cmdlet expects them in alphabetical order, not the order the documentation shows (which is the order that you’d use if using the [wmiclass] approach).
This problem will be most noticeable if the arguments are different data types. You get a nice big juicy error message that states you have a type mismatch: code 2147749893. The quick way to check the correct order of arguments is to use Get-CimClass:
$class = Get-CimClass -Namespace root\cimv2 -ClassName StdRegProv
$class.CimClassMethods["SetSTRINGvalue"].Parameters
Examining the Name of the parameters shows this:
Name
----
hDefKey
sSubKeyName
sValue
sValueName
Let’s retry it in that order:
Invoke-WmiMethod -Namespace root\cimv2 -Class StdRegProv -Name SetSTRINGvalue $hklm, $newkey, $newvalue, $newname
$reg.GetSTRINGvalue($hklm, $newkey, $newname)
And it works.
One way to avoid this confusion is to use the CIM cmdlets, which were introduced in Windows PowerShell 3.0. The problem goes away because you have to give argument name and value pairs:
To set data:
$newvalue = ‘June 2015’
Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName SetSTRINGvalue -Arguments @{hDefKey=$hklm; sSubKeyName=$newkey; sValueName=$newname; sValue=$newvalue}
And to read data:
Invoke-CimMethod -Namespace root\cimv2 -ClassName StdRegProv -MethodName GetSTRINGvalue -Arguments @{hDefKey=$hklm; sSubKeyName=$newkey; sValueName=$newname}
This still leaves us with a lot of typing. Wouldn’t it be better if we could do this:
Set-RegistrySTRING -Hive HKLM -SubKey $newkey -ValueName $newname -Value 'April 2015'
Get-RegistrySTRING -Hive HKLM -SubKey $newkey -ValueName $newname
These are cmdlets from the module that I’m going to show you how to create in the next few posts, but for now I need to clean up my registry:
Remove-RegistryKey -Hive HKLM -SubKey $newkey
Bye for now. Next time, we’ll start digging into how we can use CDXML to create registry cmdlets.
Biography
Richard Siddaway is based out of the UK. He spends his time automating anything and everything, for Kelway, Ltd. A 7-year Windows PowerShell MVP, Richard is a prolific blogger, mainly about Windows PowerShell (see Richard Siddaway's Blog). Richard has been a director at PowerShell.org since the inception of that organization, and he is a frequent speaker at Windows PowerShell user groups and conferences. He has written a number of books: PowerShell in Practice, PowerShell and WMI, PowerShell in Depth (co-author), PowerShell Dive (co-editor), and Learn Active Directory Management in a Month of Lunches, which features lots of Windows PowerShell. All of the books are available from Manning Publications.
We invite you to follow The Scripting Guys on Twitter and Facebook. If you have any questions, send an email to The Scripting Guys at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. Until then, remember eat your cmdlets every day with a dash of creativity.
Richard Siddaway, Windows PowerShell MVP and Honorary Scripting Guy