Tuesday, January 8, 2013

Powershell and WbemScripting Enumerators


I once read that is impossoble to do enumerators in Powershell.  Not true.

The below example uses GetEnumerator:

$Locator = new-Object -comObject "WbemScripting.SWbemLocator"
$svc = $Locator.ConnectServer(".", "root\cimv2")
$ob = $svc.Get("Win32_Process")
$objs = $ob.Instances_(0)

if($objs.Count -gt 0)
{
$mocEnum = $objs.GetEnumerator

$iret = $mocEnum.MoveNext()
do
{
$mo = $mocEnum.Current
$propEnum = $mo.Properties_.GetEnumerator
$ret = $propEnum.MoveNext()
do
{
$prop = $propEnum.Current
[System.String]$s = $prop.Name
write-host $s.PadRight(30, " ") : $prop.Value
}
until($propEnum.MoveNext() -eq $False)
write-host
}
until($mocEnum.MoveNext() -eq $False)
}
else
{
$mo = @($moc)[0]
$propEnum = $mo.GetEnumerator
do
{
$prop = $propEnum.Current
[System.String]$s = $prop.Name
write-host $s.PadRight(30, " ") : $prop.Value
}
until($propEnum.MoveNext() -eq $False)
}


The below example uses reflection and GetEnumerator:

$Locator = new-Object -comObject "WbemScripting.SWbemLocator"
$svc = $Locator.ConnectServer(".", "root\cimv2")
$ob = $svc.Get("Win32_Process")
$objs = $ob.Instances_(0)

if($objs.Count -gt 0)
{
    $mocEnum = $objs.GetType().InvokeMember("GetEnumerator", [System.Reflection.BindingFlags]::InvokeMethod, $Null, $objs, $Null)
    $iret = $mocEnum.MoveNext()   
    do
    {
        $mo = $mocEnum.Current
        $propEnum = $mo.Properties_.GetType().InvokeMember("GetEnumerator", [System.Reflection.BindingFlags]::InvokeMethod, $Null, $mo.Properties_, $Null)
        $ret = $propEnum.MoveNext()
        do
        {
            $prop = $propEnum.Current
            [System.String]$s = $prop.Name
            write-host $s.PadRight(30, " ") : $prop.Value
        }
        until($propEnum.MoveNext() -eq $False)
     write-host
    }
    until($mocEnum.MoveNext() -eq $False)
}
else
{
    $mo = @($moc)[0]
    $propEnum = $mo.Properties.$mo.Properties_.GetType().InvokeMember("GetEnumerator", [System.Reflection.BindingFlags]::InvokeMethod, $Null, $mo.Properties_, $Null)
   do
   {
        $prop = $propEnum.Current
        [System.String]$s = $prop.Name
        write-host $s.PadRight(30, " ") : $prop.Value
   }
   until($propEnum.MoveNext() -eq $False)
}

The below example uses reflection and __NewEnum:

$Locator = new-Object -comObject "WbemScripting.SWbemLocator"
$svc = $Locator.ConnectServer(".", "root\cimv2")
$ob = $svc.Get("Win32_Process")
$objs = $ob.Instances_(0)

if($objs.Count -gt 0)
{
$mocEnum = $objs.GetType().InvokeMember("_NewEnum", [System.Reflection.BindingFlags]::InvokeMethod, $Null, $objs, $Null)
$iret = $mocEnum.MoveNext()
do
{
$mo = $mocEnum.Current
$propEnum = $mo.Properties_.GetType().InvokeMember("_NewEnum", [System.Reflection.BindingFlags]::InvokeMethod, $Null, $mo.Properties_, $Null)
$ret = $propEnum.MoveNext()
do
{
$prop = $propEnum.Current
[System.String]$s = $prop.Name
write-host $s.PadRight(30, " ") : $prop.Value
}
until($propEnum.MoveNext() -eq $False)
write-host
}
until($mocEnum.MoveNext() -eq $False)
}
else
{
$mo = @($moc)[0]
$propEnum = $mo.Properties.$mo.Properties_.GetType().InvokeMember("_NewEnum", [System.Reflection.BindingFlags]::InvokeMethod, $Null, $mo.Properties_, $Null)
do
{
$prop = $propEnum.Current
[System.String]$s = $prop.Name
write-host $s.PadRight(30, " ") : $prop.Value
}
until($propEnum.MoveNext() -eq $False)
}


Hope this helps!  Happy Powershelling!


Using for in the world Of Powershell and WbemScripting

In the pervious post I pointed out that one way to enumerate though the object collection inside Powershell and WbemScripting is a for loop.  Looks like this:

$Locator = new-Object -comObject "WbemScripting.SWbemLocator"
$svc = $Locator.ConnectServer(".", "root\cimv2")
$ob = $svc.Get("Win32_Process")
$objs = $ob.Instances_(0)
for($x=0;$x -lt $objs.Count;$x++)
{
        $mo = $objs.ItemIndex($x)
}

Up until recently, this was something eveyone wished for.  And it is finally here. Sadly, there is no index on the propertycollection so, you still have to use a foreach loop to polish off the routine.



for($x=0;$x -lt $objs.Count;$x++)
{
     $mo = $objs.ItemIndex($x)
     foreach($prop in $obj.Properties_)
     {

     }
}

The one nice thing about Powershell is you don't have to work for the property values. These are automatically turned into strings for you.

I like that.

The next post will cover enumerators.

Powershell - WbemScripting.SWbemLocator returns a ManagementObject on single instances

Okay, for all of you out there who would love some relief from the misery of leaning Powershell by the seat of your pants and are tired of crashing and burning, let's begin with solution #1.

You have been given the task of writing a powershell script that uses WbemScripting.SWbemLocator.


$Locator = new-Object -comObject "WbemScripting.SWbemLocator"
$svc = $Locator.ConnectServer(".", "root\cimv2")
$ob = $svc.Get("win32_bios")
$objs = $ob.Instances_(0)

So at this point you want to enumerate through the object collection.  There are two ways of doing this, right?

foreach($obj in $objs)
{


}

and

for($x=0;$x -lt $objs.Count;$x++)
{

}

Acturally, there are about six more. But, none will work.

What?

Yep, none will work.  Here's why.  When Powershell sees on one iteration of an object which in this case, unless you have more than one BIOS on your board, it sees only one, an SWbemObject is returned and not a SWbemObjectSet.

You can easily deal with this situation like this:

if($objs.Count -gt 0)
{  
    foreach($obj in $objs)
    {
        foreach($prop in $obj.Properties_)
        {
   
        }
    }
}
else
{
     $obj = $objs
     foreach($prop in $obj.Properties_)
     {

     }
}

And that is your first subscription for pain relief.