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

Powershell: Learning something new everyday

Learning something new everyday is what makes programming interesting. The lesson learned today is to stop thinking powershell is "just like any other language".  Because it is not.

Consider the following:

My task is to check the local registry for the version of the OS.  So, in line, I did the following to check it:

PS C:\Users\Administrator> $HKEY_LOCAL_MACHINE = 2147483650
PS C:\Users\Administrator> $Reg = [WMIClass]"ROOT\DEFAULT:StdRegProv"
PS C:\Users\Administrator> $Key = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
PS C:\Users\Administrator> $Result = $Reg.GetStringValue($HKEY_LOCAL_MACHINE, $Key, "CurrentVersion")
PS C:\Users\Administrator> write-host $Result
System.Management.ManagementBaseObject
PS C:\Users\Administrator> $Result.GetText(0)
instance of __PARAMETERS
{
        ReturnValue = 0;
        sValue = "6.1";
};

Okay, everything looks good. In line, that is.  What happens when you want to put the routine into a function and call it anywhere.

Well, it turns out, you can try, but you'll be wasting a lot of hours.

First, let's create the function wrapper:

function get_operatingsystem_version()
{

}


Now, you would think, if you've been programming for as long as I have, that you would od the following:

$HKLM = 2147483650
$Key = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"

$Version = get_operatingsystem_version
if ($Version -ne  "Error")
{
      write-host $version
}
function get_operatingsystem_version()
{
      $Reg = [WMIClass]"ROOT\DEFAULT:StdRegProv"
      $Result = $Reg.GetStringValue($HKLM, $Key, "CurrentVersion")
      if($Result.ReturnValue -eq 0)
      {
            return  $Result.sValue          
      }
      else
      {
            return   "Error"    
      }
}
Should work, right?  Doesn't. In fact, it comes back with the following error:

The term 'get_operatingsystem_version' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\Users\Administrator\Desktop\GetOS.ps1:5 char:39
+ $Version = get_operatingsystem_version <<<<
    + CategoryInfo          : ObjectNotFound: (get_operatingsystem_version:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException
PS C:\Users\Administrator>
Why did this happen?  Because the function has not yet been seen by Powershell at the point where the call is being made.

Now, let's try it this way:

$HKLM = 2147483650
$Key = "SOFTWARE\Microsoft\Windows NT\CurrentVersion"

function get_operatingsystem_version()
{
      $Reg = [WMIClass]"ROOT\DEFAULT:StdRegProv"
      $Result = $Reg.GetStringValue($HKLM, $Key, "CurrentVersion")
      if($Result.ReturnValue -eq 0)
      {
            return  $Result.sValue          
      }
      else
      {
            return   "Error"    
      }
}

$Version = get_operatingsystem_version
if ($Version -ne "Error")
{
write-host $version
}
When I run this, I get:

PS C:\Users\Administrator> C:\Users\Administrator\Desktop\GetOS.ps1
6.1

Saturday, March 23, 2013

Creating Desktop Shortcuts using Powershell

If you've been wanting to create shortcuts using Powershell and all the examples on the web don't work then you've come to the right spot.

Aside from the code provided here, you can also download my free tool at:

http://download.cnet.com/Powershell-Shortcut-Builder/3000-2344_4-76168548.html

For Access:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "Access.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "C:\Program Files (x86)\Microsoft Office\Office15\MSAccess.exe"
$Sc.Description = "Access Link"
$Sc.Save()

For the Calculator:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "Calc.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "Calc"
$Sc.Save()

For Disk Management:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "diskmgmt.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "diskmgmt.msc"
$Sc.Description = "Disk Management Link"
$Sc.Save()

For Excel:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "Excel.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "C:\Program Files (x86)\Microsoft Office\Office15\Excel.exe"
$Sc.Description = "Excel Link"
$Sc.Save()

For MSPaint:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "MSPaint.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "MSPaint.exe"
$Sc.Description = "MSPaint Link"
$Sc.Save()

For Notepad:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "Notepad.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "Notepad.exe"
$Sc.Description = "Notepad Link"
$Sc.Save()

For Word:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "Word.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "C:\Program Files (x86)\Microsoft Office\Office15\winword.exe"
$Sc.Description = "Winword Link"
$Sc.Save()

For Wordpad:

$ws = New-Object -com WScript.Shell
$Dt = $ws.SpecialFolders.Item("Desktop")
$Scp = Join-Path -Path $Dt -ChildPath "Wordpad.lnk"
$Sc = $ws.CreateShortcut($Scp)
$Sc.TargetPath = "C:\Program Files (x86)\Windows NT\Accessories\Wordpad.exe"
$Sc.Description = "Wordpad Link"
$Sc.Save()

Also, if you want to add a link to this blog:

$ws = New-Object -comObject WScript.Shell
$Dt = $ws.SpecialFolders.item("Desktop")
$URL = $ws.CreateShortcut($Dt + "\Powershell Pain Relief.url")
$URL.TargetPath = "http://powershellpainrelief.blogspot.com"
$URL.Save()

Getting the local machine's IP Address

If you haven't seen this before, that's okay because I hadn't either:

[System.Net.Dns]::GetHostbyAddress($IP).HostName

The problem I have with this call is that it is mentally deprived. First, how can this be called without already knowing the IP address? It can't meaning, you already have a method to obtain the IP Address.  Why would you want to bother getting the HostName?

[System.Net.Dns]::GetHostbyName("WIN-SEN3DGN4RRM").HostName

This returns my FQDN:   WIN-SEN3DGN4RRM.Mary.Local

Same call but with a different property:

[System.Net.Dns]::GetHostbyName("WIN-SEN3DGN4RRM").AddressList[0].IPAddressToString

Returns: 10.1.10.14

ADSI getting the current domain


Really easy to get the active domain:

$iret = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices")
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain

ManagementClass For Each

Here's an example of using the ManagementClass and enumerating with for each:

[System.Management.ManagementClass]$mc = New-Object System.Management.ManagementClass
$mc.Path.NamespacePath = "root\cimv2"
$mc.Path.Classname = "Win32_Process"
$mc.Scope.Options.Authentication = 6
$mc.Scope.Options.Imersponation = 3
$moc = $mc.GetInstances
foreach($mm in $moc)
{
    foreach($prop in $mo.Properties)
    {   
        $v = 30
        #$v = $v - $prop.Name.ToString().Length
        write-host $prop.Name.PadRight($v, " ") : $prop.Value
    }
   
}

Do Until Example

If your like me, Do until can dive you nuts.  Why? Becuase its the opposite of what you think the logic should be.  Here's an example.

$mos = new-object System.Management.ManagementObjectSearcher("Select * From Win32_Process")
$mos.Scope.Path.NamespacePath = "root\cimv2"
$mos.Scope.Path.Classname = "Win32_Process"
[System.Management.ManagementObjectCollection]$moc = $mos.Get()
$mocEnum = $moc.GetEnumerator()
$iret = $mocEnum.MoveNext()
do{
    [System.Management.ManagementBaseObject]$mo = $mocEnum.Current
    [System.Management.PropertyDataCollection]$propset = $mo.Properties
    $propEnum = $propset.GetEnumerator()
    $iret = $propEnum.MoveNext()
    do{
         [System.Management.PropertyData] $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)

The MoveNext returns true until there is nothing left to enumerate through. So what is being said here is while MoveNext equals true allow the routine to loop. Here's the same thing with Do While:

$mos = new-object System.Management.ManagementObjectSearcher("Select * From Win32_Process")
$mos.Scope.Path.NamespacePath = "root\cimv2"
[System.Management.ManagementObjectCollection]$moc = $mos.Get()
$mocEnum = $moc.GetEnumerator()
$iret = $mocEnum.MoveNext()
do{
[System.Management.ManagementBaseObject]$mo = $mocEnum.Current
[System.Management.PropertyDataCollection]$propset = $mo.Properties
$propEnum = $propset.GetEnumerator()
$iret = $propEnum.MoveNext()
do{
[System.Management.PropertyData] $prop = $propEnum.Current
[System.String]$s = $prop.Name
write-host $s.PadRight(30, " ") : $prop.Value
}
    while($propEnum.MoveNext())
    write-host
}

while($mocEnum.MoveNext() )


Registry Identity Check

Excuse me, mister computer, just who in hades are you?

Kind of helps to answer that question with identity keys that are created in the registry and then discovered

                       function Check-machine-registry
                     {
                             try
                            {
                                [System.Boolean]$rt = $false
                                $Name = "KeyValueName"
                                $Path = "HKLM:\SOFTWARE\Some registry key path"
                                $key = Get-Item $Path  -ErrorAction SilentlyContinue
                                $value = $key.GetValue($Name)
                                if ($Value -ne "KeyValue")
                                {
                                    return $rt
                                }
                                $Name1 = "KeyValueName"
                                $Path1 = "HKLM:\SOFTWARE\Some registry key path"
                                $key1 = Get-Item $Path  -ErrorAction SilentlyContinue
                                $Value1 = $key1.GetValue($Name1)
                                if ($Value1 -ne "KeyValue")
                                {
                                    return $rt
                                }
                                $rt = $true
                                return $rt 
                            }
                            catch
                            {
                                return $rt
                            }
                    }

From net user with love

   function get-service-account-information
                        {
                            for($c=0;$c -lt 3;$c++)
                            {
                                $d = net user $UserNames[$c] /DOMAIN
                                Foreach($a in $d)
                                {
                                    if($a.contains("Account active") -eq $true)
                                    {
                                        $Active = $a.Replace("Account active","")
                                        $Active = $Active.Trim()
                                        if($Active.contains("Yes") -eq $true)
                                        {           
                                            $strActive[$c] = "Yes"
                                        }
                                    }
                                    else
                                    {
                                        $strActive[$c[ = "No"
                                    }
                                    if($a.contains("Account expires") -eq $true)
                                    {
                                        $AExpires = $a.Replace("Account expires","")
                                        $AExpires = $AExpires.Trim()
                                        if($AExpires.contains("Never") -eq $false)
                                        {
                                     $AExpires = $a.Replace("Account expires","")
                                            $AExpires = $AExpires.Trim()   
                                            $dt = [DateTime]$AExpires
                                            $f = $([DateTime]::Now - $dt)
                                            [System.String]$g = $f.TotalMilliSeconds/86400000
                                            $pos = $g.IndexOf(".")
                                            $g = $g.Substring(0, $pos)
                                            $strAccountExpires[$c] = $g
                                        }
                                    }
                                    if($a.contains("Password expires") -eq $true)
                                    {
                                        $PExpires = $a.Replace("Password expires","")
                                        $PExpires = $PExpires.Trim()   
                                        $dt = [DateTime]$PExpires
                                        $strPasswordExpiresDate[$c] = "Password is set to expire on: " + $PExpires
                                        $f = $($dt - [DateTime]::Now)
                                        [System.String]$g = $f.TotalMilliSeconds/86400000
                                        $pos = $g.IndexOf(".")
                                        $g = $g.Substring(0, $pos)
                                        $g = $g.Trim()
                                        $strPasswordExpires[$c] = $g
                                    }
                                    if($a.contains("Password last set") -eq $true)
                                    {
                                        $PLastSet = $a.Replace("Password last set","")
                                        $PLastSet = $PLastSet.Trim()   
                                        $dt = [DateTime]$PLastSet
                                        $strPasswordLastSet[$c] = $dt
                                    }
                                    if($a.contains("User may change password") -eq $true)
                                    {
                                        $PChangeable = $a.Replace("User may change password","")
                                        $PChangeable = $PChangeable.Trim()
                                        if($PChangeable.contains("Yes") -eq $true)
                                        {           
                                            $strPasswordChangeable[$c] = "true"
                                        }
                                        else
                                        {
                                            $strPasswordChangeable[$c] = "false"
                                        }
                                    }
                                }
                            }
                        }  

Using WMI WbemScripting.SWbemLocator


More code, less talking.

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

Multi Dimantional Arrays Framework Style

I know the standard Powershell array creation looks like this: $myArray = @(), but I wanted something more. Something with some teeth.

One Dimensional Array:
$myarray = [Array]::CreateInstance([String], 10)

Two Dimensional Array:
$myarray = [Array]::CreateInstance([Object], 10, 10)

Three Dimensional Array:
$myarray = [Array]::CreateInstance([Boolean], 10, 10, 10)

Not sure why anyone would want to create a 10 x 3 matrix but I wanted to show that the type of object you want to use goes first. followed by the amount of dimensions and their 1 based amount.

One based?

Yep. Try doing 10. 9 is the limit minus 1 for 0 equals 10

$UserNames = [Array]::CreateInstance([String], 3)
$UserNames[0] = "First User"
$UserNames[1] = "Second User"
$UserNames[2] = "Third User"


An additioanl advantage here is that you can use GetLength

for($c=0;$c -lt $UserNames.GetLength(0);$c++)
{

write-host $UserNames.GetValue($c)
}

Fun with the .Net Framework

Okay, admittedly, there are a slew of blogs and web pages that cover exactly the same thing.

I just do it a bit differently.

Here's an example of what I mean:

Someone makes a big deal about the below information:

[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Hello World")


I do this:

#This line creates a WScript.Shell COM object I will use to set the current directory. 
$ws = new-object -comobject WScript.Shell

#This line loads SCOM 2007 SDK Operations Manager Framework
$iret =[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.EnterpriseManagement.OperationsManager")
#This line creates a pointer to a specific ManagementPack I want to unseal
$mp = new-object Microsoft.EnterpriseManagement.Configuration.ManagementPack("C:\Program Files\System Center Operations Manager 2007\System.Library.mp")

#This line creates a pointer to the XmlWriter I will use to write out the XML with
$xmlWriter = new-object Microsoft.EnterpriseManagement.Configuration.IO.ManagementPackXmlWriter($ws.CurrentDirectory)

#This line writes the ManagementPack out in xml Format and tells me where the file is
$filename = $xmlWriter.WriteManagementPack($mp)

#This line displays a messagebox with the newly created filename and path
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show($filename)


I could have just as easily used: Write-Host $filename and been done with it.

But what is really going on here with: [System.Windows.Forms.MessageBox]::Show($filename)?

Well, for one thing, take a look at the image below:


The object, System.Windows.Forms.MessageBox, is created and is told to invoke a method called show.

Since this method takes many optional parameters, we use the one which enables us to use just one.




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.