I recently came across an issue and a solution for it regarding the use of the StdRegProv which enables us to access the registry via all other languages except Powershell.
Lets take a look at what I'm talking about:
The MOF looks like this:
uint32 EnumKey( [in, optional] uint32 hDefKey = HKEY_LOCAL_MACHINE, [in] string sSubKeyName, [out] string sNames[] );
We call it by creating the HKEY_ value in hex or dec:
Const HKEY_CLASSES_ROOT = &H80000000
or
Const HKEY_CLASSES_ROOT = 2147483648
Dim iret
Dim Names()
Dim sKey
sKey = "clsid"
Set oReg = GetObject("winmgmts:\\.\root\cimv2").Get("StdRegProv")
oReg.EnumKey HKEY_CLASSES_ROOT, sKey, Names
For each Name in Names
wscript.echo Name
Exit For
Next
Works flawlessly.
Now, let's turn this into a powershell script:
$HKEY_CLASSES_ROOT = 2147483648
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
$sKey = "clsid"
$oReg = Get-WmiObject -namespace root\cimv2 -class StdRegProv
$iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
Foreach($Name in $Names)
{
write-host $Name
break
}
As it turns out, you can't call this as an instance of a class:
You cannot call a method on a null-valued expression.
At C:\Users\Administrator\Desktop\testregcode.ps1:9 char:1
+ $iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You need to call it as a class:
$HKEY_CLASSES_ROOT = 2147483648
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
Foreach($Name in $Names)
{
write-host $Name
break
}
But you will still get an error:
Cannot find an overload for "EnumKey" and the argument count: "3".
At C:\Users\Administrator\Desktop\testregcode.ps1:9 char:1
+ $iret = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey, $Names)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Okay, so what is going on here? Well, it turns out that you pass in the correct IN parameters as you normally would and get passed back out a System.Management.ManagementBaseObject.
$HKEY_CLASSES_ROOT = 2147483648
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
[System.Object]$retValues
$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$retValues = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey)
foreach($prop in $retValues.Properties)
{
$prop.Name
}
This will return:
ReturnValue
sNames
You will see this in examples on the web:
$HKEY_CLASSES_ROOT = 2147483648
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
[System.String[]]$strValues
$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$strValues = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey).snames
foreach($val in $strValues)
{
write-host $val
}
This will return every subkey in HKEY_CLASSES_ROOT\clsid. With that said, you really need to parse for the return Value as it will tell you whether or not the call was successful or not.
$HKEY_CLASSES_ROOT = 2147483648
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
$OutVal
$sKey = "clsid"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$OutVal = $oReg.EnumKey($HKEY_CLASSES_ROOT, $sKey)
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$sNames = $OutVal.Properties.Item("sNames").Value
foreach($sName in $sNames)
{
write-host $sName
}
}
Here's an example of getting a DWORD value:
$HKEY_LOCAL_MACHINE = 2147483650
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
$OutVal
$sKey = "Software\Microsoft\Windows NT\CurrentVersion"
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$OutVal = $oReg.GetDWordValue($HKEY_LOCAL_MACHINE, $sKey, "InstallDate")
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("uValue").Value
$vs = '{0:x}' -f $Value
$vs = "0x" + $vs + "(" + $Value + ")"
write-host $vs
}
Now, let's do the Reg_Binary:
$HKEY_LOCAL_MACHINE = 2147483650
[System.Int32]$iret
[System.String[]]$Names
[System.String]$sKey
[System.Management.ManagementBaseObject]$OutVal
$sKey = "Software\Microsoft\Windows NT\CurrentVersion"
[System.String]$vs = ""
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$Name = "DigitalProductId"
$OutVal = $oReg.GetBinaryValue($HKEY_LOCAL_MACHINE, $sKey, $Name)
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("uValue").Value
foreach($v in $Value)
{
$vv = '{0:x}' -f $v
if($vv.ToString().Length -eq 1)
{
$vv = "0" + $vv
}
if($vs -ne "")
{
$vs = $vs + ","
}
$vs = $vs + $vv
$vv = ""
}
write-host $vs
}
Now, let's enumerate through Values:
$HKEY_LOCAL_MACHINE = 2147483650
[System.Int32[]]$DataTypes
[System.String[]]$ValueNames
[System.String]$sKey
[System.Management.ManagementBaseObject]$OutVal
$sKey = "SYSTEM\CurrentControlSet\Control\Session Manager"
[System.String]$vs = ""
$oReg = [WMICLASS]"root\cimv2:StdRegProv"
$Name = "DigitalProductId"
$OutVal = $oReg.EnumValues($HKEY_LOCAL_MACHINE, $sKey)
[System.String]$Value
[System.String]$v
[System.String]$vv
$ValueNames = $outVal.Properties.Item("sNames").Value
$DataTypes = $outVal.Properties.Item("Types").Value
for($x=0;$x -lt $DataTypes.GetLength(0); $x++)
{
switch ($DataTypes[$x])
{
1{
$OutVal = $oReg.GetStringValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("sValue").Value
}
Write-Host $ValueNames[$x] REG_SZ $Value
}
2{
$OutVal = $oReg.GetExpandedStringValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("sValue").Value
}
Write-Host $ValueNames[$x] REG_EXPAND_SZ $Value
}
3{
$OutVal = $oReg.GetBinaryValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("uValue").Value
foreach($v in $Value)
{
$vv = '{0:x}' -f $v
if($vv.ToString().Length -eq 1)
{
$vv = "0" + $vv
}
if($vs -ne "")
{
$vs = $vs + ","
}
$vs = $vs + $vv
$vv = ""
}
Write-Host $ValueNames[$x] REG_BINARY $vs
}
}
4{
$vs = ""
$OutVal = $oReg.GetDWordValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("uValue").Value
foreach($v in $Value)
{
$vv = '{0:x}' -f $v
if($vs -ne "")
{
$vs = $vs + ","
}
$vs = $vs + $vv
$vv = ""
}
Write-Host $ValueNames[$x] REG_DWORD $vs
}
}
7{
$OutVal = $oReg.GetMultiStringValue($HKEY_LOCAL_MACHINE, $sKey, $ValueNames[$x])
if($OutVal.Properties.Item("ReturnValue").Value -eq 0)
{
$Value = $OutVal.Properties.Item("sValue").Value
}
Write-Host $ValueNames[$x] REG_SZ $Value
}
}
}
Would you happen to know what would cause:
ReplyDeleteException calling "EnumKey" : "Access denied " ?
I get this error rarely when using similar code to yours and the error I catch doesn't say much else about it.
There are areas in the registry where Microsoft has flagged it for their use only.
ReplyDeleteNothing you can do about that and you will get an Access Denied.
Are you calling the enumerator recursively? It is possible that the issue is due to that.
I'd have to see your code in order to determine if there is a logical flow issue.