Tuesday, December 24, 2013

Solution: Using StdRegProv


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  
            }
        }
}






 


Friday, October 11, 2013

Dealing With CSV Files


Let's take a look at some csv  directly from Microsoft:

Name,Department,Title
Pilar Ackerman,Research,Manager
Jonathan Haas,Finance,Finance Specialist
Ken Myer,Finance,Accountant

Pop that into notepad and then open it up in Powershell using the below code:

$csvFile = "C:\Users\Administrator\Desktop\minor.csv"
Import-CSV $csvFile |
foreach-object
{
    write-host $_.Title
}

Should give me Manager, Finance Specialist and Accountant. Wrong!

As it turns out, | Doesn't work in V3 of Powershell and removing it produces:


Name                                    Department                              Title
----                                    ----------                              -----
Pilar Ackerman                          Research                                Manager
Jonathan Haas                           Finance                                 Finance Specialist
Ken Myer                                Finance                                 Accountant


The following code works to create what used to work in Powershell V2.


$csvFile = "C:\Users\Administrator\Desktop\minor.csv"
$file_data = @(Import-CSV -Path $csvFile)
foreach($obj in $file_data)
{
     $loc = $obj.Name
     Write-host $loc
}

This will produce:
Pilar Ackerman
Jonathan Haas
Ken Myer

Saturday, April 6, 2013

DAO ain't DOA

The latest build that I know about is DAO.DBEngine.120.

Its a bit tricky to write in powershell. Below is the code just in case anyone is looking for it:


    $iret = [System.Reflection.Assembly]::LoadWithPartialName("System.Management")
    $mc = new-object System.Management.ManagementClass
    $mc.Path.NamespacePath = "root\cimv2"
    $mc.Path.ClassName = "Win32_Process";
    $mc.Scope.Options.Authentication = [System.Management.AuthenticationLevel]::PacketPrivacy;
    $mc.Scope.Options.Impersonation = [System.Management.ImpersonationLevel]::Impersonate;
    $moc = $mc.GetInstances()

    $DBEngine = new-Object -comobject DAO.DBEngine.120
    $DBEngine.CreateDatabase("C:\Melody.mdb",  ";LANGID=0x0409;CP=1252;COUNTRY=0", 64)
    $db = $DBEngine.OpenDatabase("C:\Melody.mdb", $false, $false, "")
    $rs = $db.OpenRecordset("Properties");
    foreach($mo in $moc)
    {
         $rs.AddNew()
         $x=0
         $Fields = $rs.GetType().InvokeMember("Fields", [System.Reflection.BindingFlags]::GetProperty, $null, $rs, $null)
         foreach($Field in $Fields)
         {
                [System.String] $Name = $Field.Name
                [System.String] $Value = $mo.Properties.Item($Name).Value
                $Field.Value = $Value
         }
         $x=0
         $rs.Update()
    }

Create a SQL Database using Powershell and SQLClient

First, I am by far no expert when it comes to knowing all the ins and outs of SQL Server.  What I do know is this, you can create a database, create and populate a table and then render the information in a variety of ways using Powershell.


$iret = [System.Reflection.Assembly]::loadWithPartialName("System.Data")
$con = new-object System.Data.SqlClient.SqlConnection
$con.ConnectionString="Data Source=.;Integrated Security=sspi;"
$con.Open()

$cmd = new-object System.Data.SqlClient.SqlCommand()
$cmd.Connection = $con
$cmd.CommandType = [System.Data.CommandType]::Text
$cmd.CommandText = "CREATE Database DataOne"
$cmd.ExecuteNonQuery

You can do the same thing with Odbc and OleDb as well.

Here's the OleDb example:

$iret = [System.Reflection.Assembly]::loadWithPartialName("System.Data")
$con = new-object System.Data.OleDb.OleDbConnection
$con.ConnectionString="Provider=SQLOLEDB;Data Source=.;Integrated Security=sspi;"
$con.Open()

$cmd = new-object System.Data.OleDb.OleDbCommand()
$cmd.Connection = $con
$cmd.CommandType = [System.Data.CommandType]::Text
$cmd.CommandText = "CREATE Database DataOne"
$cmd.ExecuteNonQuery



And when using Odbc:

$iret = [System.Reflection.Assembly]::loadWithPartialName("System.Data")
$con = new-object System.Data.Odbc.OdbcConnection
$con.ConnectionString="Driver={SQL Server};Server=.;Integrated Security=sspi;"
$con.Open()

$cmd = new-object System.Data.Odbc.OdbcCommand()
$cmd.Connection = $con
$cmd.CommandType = [System.Data.CommandType]::Text
$cmd.CommandText = "CREATE Database DataOne"
$cmd.ExecuteNonQuery

Tomorrow, I'll show you how to create and populate the table.

Friday, April 5, 2013

To Text Or Not To Text -- I just want to be in control

If you are like me -- another programmer forced to learn Powershell -- you want to be in control of the output.  What output format it is in and just how easy it is two read comes quickly to mind.
With that thought in mind, I decided to see if I could get the textwriter to work for me.

I did this by using the following code:

[System.IO.TextWriter]$txtstream = new-object System.IO.Streamwriter([System.Environment]::CurrentDirectory + "\WillItWork.txt")

That worked perfectly fine. To close this object out:

$txtstream.Flush()
$txtstream.Close()
$txtstream = $null

The programer's mindset text formats include: csv, excel spreadsheets, hta, html, attribute xml, element xml, element xml for xsl, and xsl -- just to name a few. Some of these can also be put into different formats such as Single and multi line horizontal and vertical views as well as the text being formatted for a more table like view of the data.

Please use the 32 bit version of Powershell for the following code as the code will fail in the 64 bit version. Jet is not supported in the 64 bit world.

$con = new-object -comobject ADODB.Connection
$con.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\nwind.mdb")
$rs = new-object -comobject ADODB.Recordset
$rs.ActiveConnection = $con
$rs.CursorLocation = [ADODB.CursorLocationEnum]::adUseClient
$rs.LockType = [ADODB.LockTypeEnum]::adLockOptimistic
$rs.let_Source("Select * From Products")
$rs.Open()
 

Now that I have an open recordset, I want to do something with the information. So, I'm going to create some xml:

[System.IO.TextWriter]$txtstream = new-object System.IO.Streamwriter([System.Environment]::CurrentDirectory + "\product.xml")
$con = new-object -comobject ADODB.Connection
$con.Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\nwind.mdb")
$rs = new-object -comobject ADODB.Recordset
$rs.ActiveConnection = $con
$rs.CursorLocation = [ADODB.CursorLocationEnum]::adUseClient
$rs.LockType = [ADODB.LockTypeEnum]::adLockOptimistic
$rs.let_Source("Select * From Products")

$rs.Open()
$txtstream.WriteLine("<?xml version='1.0' encoding='iso-8859-1'?>")
$txtstream.WriteLine("<data>")
$txtstream.WriteLine("<Products>")
$rs.MoveFirst();
for($y=0;$y -lt $rs.RecordCount;$y++)
{
     $txtstream.WriteLine("<Product>")
     for($x=0;$x -lt $rs.Fields.Count;$x++)
     {
         $fld = $rs.Fields[$x]
         $Name = $fld.GetType().InvokeMember("Name", [System.Reflection.BindingFlags]::GetProperty, $null, $fld, $null)
         $Value = $fld.GetType().InvokeMember("Value", [System.Reflection.BindingFlags]::GetProperty, $null, $fld, $null)
         $txtstream.WriteLine("<" + $Name + "><![CDATA[" + $Value + "]]></" + $Name  + ">")
     }
     $txtstream.WriteLine("</Product>")
     $rs.MoveNext()
}
$txtstream.WriteLine("</Products>")
$txtstream.WriteLine("</data>")
$txtstream.Flush()
$txtstream.Close()
$txtstream = $null









Wednesday, April 3, 2013

Get-WMIObject To be or not to be -- a collection or an object


Consider this code:

$mystr = ""
$ws = new-object -comobject WScript.Shell
$path = $ws.CurrentDirectory + "\Win32_Process.csv"
$fso = new-object -comobject Scripting.FileSystemObject
$txtstream = $fso.OpenTextFile($path, 2, $true, -2)
$moc = Get-WMIObject -namespace root\cimv2 -class Win32_Process
$mocEnum = $moc.GetType().InvokeMember('GetEnumerator', 'InvokeMethod',$Null, $moc, $Null)
while($mocEnum.MoveNext())
{
    $obj =  $mocEnum.Current
    foreach($prop in $obj.Properties)
    {  
        if($mystr -ne "")
        {
            $mystr += ","
        }
        $mystr += $prop.Name 
    }
    $txtstream.WriteLine($mystr)
    $mystr = ""
    break     
}
$mocEnum.Reset()

while($mocEnum.MoveNext())
{
    $obj = $mocEnum.Current
    foreach($prop in $obj.Properties)
    {  
        if($mystr -ne "")
        {
            $mystr += ","
        }
        $tstr = '"'
        $tstr += $prop.value
        $tstr += '"'
        $mystr += $tstr 
    }
    $txtstream.WriteLine($mystr)
    $mystr = ""        


As it stands, this code works well to create a csv file.  And, yes, you can do the same using the PSObject. The problem is, the code assumes the Get-WMIObject will return a collection of objects and in some cases it will not.

Exception calling "InvokeMember" with "5" argument(s): "Method 'System.Management.ManagementObject.GetEnumerator' not found."
At C:\Users\Administrator\Desktop\Test.ps1:2 char:39
+ $mocEnum = $moc.GetType().InvokeMember <<<< ('GetEnumerator', 'InvokeMethod',$Null, $moc, $Null)
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

You cannot call a method on a null-valued expression.
At C:\Users\Administrator\Desktop\Test.ps1:3 char:24
+ while($mocEnum.MoveNext <<<< ())
+ CategoryInfo : InvalidOperation: (MoveNext:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull



And, by the way, the code works fine using foreach enumerations of the objects.




Powershell Access The Full monty