Sunday, March 2, 2014

Requesting WMI registry data on a 64-bit platform using a 32-bit PowerShell process

We've all been there before. You've learned "all there is to know" about the differences between x86 and x64 Windows. When working with the file system, look for "Program Files (x86)" or "SysWow64". When working with the registry, look for "Wow6432Node" to find your 32-bit application data. Life's good and you go on coding in your 64-bit ISE world, confident that you'll avoid cross-architecture pitfalls with the knowledge you've learned. Everything looks good, right?


The Problem

You find yourself needing to run your script with SCCM 2007 which uses a 32-bit client agent. Your script accesses both 32-bit and 64-bit registry locations using that trusty "Wow6432Node" key you learned about. Suddenly you realize that nothing is working as you expected when running on a 64-bit machine. This is because a 32-bit process knows nothing of the Wow6432Node that you are hard coding in your script.


The Solution

To get around this, I use the .Net StdRegProv class. This class allows you to set the architecture or "alternate registry view" as part of your registry query. To find out more about requesting WMI data on a 64-bit platform, click here.

The following example can be run in either x86 or x64 processes and is compatible with PowerShell 2.0 or later.
#-----------------------------
#--  Get-RegKey           
#-----------------------------            
Function Get-RegKey {            
    [cmdletbinding()]            
    Param(            
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]            
        [int] $Arch, # registry view to use            
                    
        [Parameter(ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)][ValidateNotNullOrEmpty()]            
        [string] $Computer = ".", # computer to query            
            
        [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]            
        [string] $KeyPath, # path to the key to find            
                    
        [Parameter()] [ValidateNotNullOrEmpty()]            
        [object] $WMIObject # WMI connection object passed by another registry cmdlet                          
    )            
            
 Begin {            
        # Parse the Keypath            
        $i = $keyPath.IndexOf(":")            
        $Hkey = $keyPath.Substring(0,$i)            
        $KeyPath = $KeyPath.Substring($i + 2)            
    }            
            
 Process {            
  try {                    
            # Create a WMI connection object if one is not passed to the function already            
            If ($WMIObject -eq $null) {            
                # Create WMI connection object            
                $objswbem = New-Object -ComObject "WbemScripting.SWbemNamedValueSet"            
                $objswbem.Add("__ProviderArchitecture", $Arch) | Out-null            
                $objswbem.Add("__RequiredArchitecture", $True) | Out-null            
                $ObjLocator = New-Object -ComObject "Wbemscripting.SWbemLocator"            
                $objServices = $objLocator.ConnectServer($Computer,"root\Default",$null,$null,$null,$null,$null,$objswbem)            
                $objReg = $objServices.Get("stdRegProv")            
            } Else {            
                # Use the passed WMI connection object            
                $objReg = $WMIObject                
            }            
                        
            # Load the Enumkey method into the inparams            
            $Inparams = $objreg.Methods_.Item("EnumKey").Inparameters            
                        
            # Set the Hkey value            
            switch ($Hkey) {            
                “HKCR” {$HkeyVal = "&h80000000"} #HKEY_CLASSES_ROOT             
                “HKCU” {$HkeyVal = "&h80000001"} #HKEY_CURRENT_USER            
                “HKLM” {$HkeyVal = "&h80000002"} #HKEY_LOCAL_MACHINE             
                "HKU" {$HkeyVal = "&h80000003"}  #HKEY_USERS                             
                "HKCC" {$HkeyVal = "&h80000005"} #HKEY_CURRENT_CONFIG            
                "HKDD" {$HkeyVal = "&h80000006"} #HKEY_DYN_DATA                            
            }            
            
            # Load the parameters and execute method            
            $inparams.properties_.item("Hdefkey").Value = $HkeyVal            
            $inparams.properties_.item("sSubKeyName").Value = $KeyPath            
            $Outparams = $objReg.ExecMethod_.Invoke("EnumKey", $Inparams,$null,$objswbem)            
                        
            # Output the results                           
            if (($Outparams.Properties_ | where {$_.name -eq "ReturnValue"}).Value -eq 0) {              
                $OutputObj = New-Object -Type PSObject            
                $OutputObj | Add-Member -MemberType NoteProperty -Name "Computer" –Value $Computer            
                $OutputObj | Add-Member -MemberType NoteProperty -Name "Architecture" –Value "$Arch-bit"
                $OutputObj | Add-Member -MemberType NoteProperty -Name "KeyPath" –Value "$HKey`:\$KeyPath"
                $OutputObj | Add-Member -MemberType NoteProperty -Name "Exist" –Value $True            
                $OutputObj             
              
            } else {                
                $OutputObj = New-Object -Type PSObject            
                $OutputObj | Add-Member -MemberType NoteProperty -Name "Computer" –Value $Computer            
                $OutputObj | Add-Member -MemberType NoteProperty -Name "Architecture" –Value "$Arch-bit"
                $OutputObj | Add-Member -MemberType NoteProperty -Name "KeyPath" –Value "$HKey`:\$KeyPath"
                $OutputObj | Add-Member -MemberType NoteProperty -Name "Exist" –Value $False            
                $OutputObj               
            }                              
        } catch {                    
        #throw “Failed to retrieve keys in: ‘$KeyPath’. The error was: ‘$_’.”             
            $OutputObj = New-Object -Type PSObject            
            $OutputObj | Add-Member -MemberType NoteProperty -Name "Computer" –Value $Computer            
            $OutputObj | Add-Member -MemberType NoteProperty -Name "Architecture" –Value $_            
            $OutputObj | Add-Member -MemberType NoteProperty -Name "KeyPath" –Value "$HKey`:\$KeyPath"
            $OutputObj | Add-Member -MemberType NoteProperty -Name "Exist" –Value ""            
            $OutputObj                         
  }            
        }            
            
    End {}            
                
    <#
  .SYNOPSIS
   Checks if registry key exists

  .DESCRIPTION
   Checks if registry key exists using the enumkey method of the StdRegProv

  .PARAMETER Arch
   Registry architecture or view to use for query

  .PARAMETER Computer
   Computer name to query registry from. If none provided searches local computer.
   
  .PARAMETER KeyPath
   Registry key path to find
  
  .EXAMPLE
   PS C:\> Get-RegKey 64 mycomputer "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore"
            
        .EXAMPLE
   PS C:\> Get-RegKey -Arch 64 "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore"

  .EXAMPLE
   PS C:\> Get-RegKey 64 my computer "HKLM:\Software\Alps\Apoint"

  .INPUTS
   System.String

  .OUTPUTS
   
  .NOTES
                        Part of the RegistryCmdlets.ps1 script by Jeff Pollock
                        http://lifeinpowershell.blogspot.com 
                        http://gallery.technet.microsoft.com/Powershell-Registry-19689888                                                                 
 #>                        
}

As you can see there is quite a bit of coding just to properly get a simple RegKey and there are many other registry operations yet to cover. Fortunately I've coded the most useful ones for you. You can get them in the link at the end of this post. They are well commented so I won't go into detail about them here other than to provide the following.

List of the cmdlets provided in the script:

  • Get-RegKey()
  • Get-RegSubKeys()
  • Set-RegKey()
  • Remove-RegKey()
  • Get-RegValue()
  • Get-RegSubValues()
  • Set-RegValue
  • Remove-RegValue()
  • SearchRegValue()

  • Cmdlet features:

  • Both x86 and x64 compatible
  • Can accept a WMI registry object as a parameter to avoid creating multiple WMI connections.
  • Fully pipeline enabled

  • Over the next few posts I will talk a little more about some of the routines used in these cmdlets. For now, give them a try and let me know how it goes.

    Here is a link to the script on the Technet Gallery: