Compartilhar via


PowerShell, WMI and formatting

I’ve been doing a ton of PowerShell recently, in which is part of the reason why the number of blog posts I’ve made has been down. One of the jobs has been to go back to some of the code I wrote a while back and examine how I handle WMI objects. At the time I wrote my contribution to the OCS resource kit last year I was pretty new to PowerShell and I wish I knew what I know now before I started.

Actually where I started with OCS and PowerShell was looking at someone else’s code where they had for loop with a bunch of write-host statements to output WMI objects to the screen. I said “don’t be daft, use | format-table” and the next thing I knew I was being asked to bring my obvious expertise to bear on the project (A Polite form of “if you’re so clever you do it then” ). So I started with a Bunch of get-thing functions. These would be in the form Get-WMIObject –Class Thing | format table. I quickly realised that wasn’t right because once it is formatted as a table you can’t use the object. So for every class of “thing” I made the get-thing function  return the WMI object(s) and had a list-thing function. One of the testers didn’t like the unformatted WMI objects if he used get- instread of list and I told him that was just how things had to be.

Lets move forward to recent history. I was looking at the Get-process function in PowerShell, when you run it it looks like this.

 PS C:\Users\jamesone\Documents\windowsPowershell> get-process -Name winword  
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName  
-------  ------    -----      ----- -----   ------     -- -----------  
    931     123    85380     149360   506 ...86.16    264 WINWORD

Only one thing wrong with that – if you send it to get-member you see it is a “System.Diagnostics.Process” object but those properties “NPM(K)” and the rest the don’t exist. Curious. I already knew PowerShell has some XML files which let me spot-weld properties onto objects, so I went for a little search using my favourite function Select-String.

 PS C:\Users\jamesone\Documents\windowsPowershell> cd $pshome

PS C:\Windows\System32\WindowsPowerShell\v1.0> select-string -path *.ps1xml -simplematch "System.Diagnostics.Process" 
DotNetTypes.format.ps1xml:370:                <TypeName>System.Diagnostics.ProcessModule</TypeName> 
DotNetTypes.format.ps1xml:404:                <TypeName>System.Diagnostics.Process</TypeName>
DotNetTypes.format.ps1xml:405:                <TypeName>Deserialized.System.Diagnostics.Process</TypeName>

DotNetTypes.format.ps1xml:551:                <TypeName>System.Diagnostics.Process</TypeName>

DotNetTypes.format.ps1xml:598:                <TypeName>System.Diagnostics.Process</TypeName>

DotNetTypes.format.ps1xml:1090:                <TypeName>System.Diagnostics.Process</TypeName>

types.ps1xml:133:        <Name>System.Diagnostics.ProcessModule</Name>

types.ps1xml:327:        <Name>System.Diagnostics.Process</Name>

types.ps1xml:430:        <Name>Deserialized.System.Diagnostics.Process</Name>

The files themselves are signed so they shouldn’t be changed, but you can look at them.

In Types.Ps1XML there is a section starting at line 327 which defines the “spot-welded” properties. Have a look at a process in you’ll see alias properties like “NPM” which aren’t part of the definition of the .NET object, that’s where they come from.

In DotNetTypes.format.ps1xml there is a section which describes how the object should be formatted, table or list, column widths and headings, and properties or code blocks. It’s all written up on MSDN. So I could use this as the basis for my own XML file: load that using Update-FormatData and… job done. 

The gist of the XML file is it looks like this

 <Configuration>

    <ViewDefinitions>  
     </ViewDefinitions>  
</Configuration>

Inside viewDefinitions there are one or more <view> objects. Which look like this, the key piece is the name of the type, as you can see this one is for a virtual machine in Hyper-V, and this one is formatted using a table.

   <View>
    <Name>Msvm_ComputerSystem</Name>

    <ViewSelectedBy>
      <TypeName>System.Management.ManagementObject#root\Virtualization\Msvm_ComputerSystem</TypeName>
    </ViewSelectedBy>
     <TableControl>
       <TableHeaders>
      </TableHeaders>
       <TableRowEntries>
          <TableRowEntry>
          </TableRowEntry>
        </TableRowEntries>
     </TableControl>
   </View>

The Table headers section contains one entry for each column

   <TableColumnHeader>
    <Label>Up-Time (mS)</Label>
    <Width>12</Width>
    <Alignment>left</Alignment>
  </TableColumnHeader>

And the TableRowEntry section contains a corresponding entry

   <TableColumnItem>
    <PropertyName>OnTimeInMilliseconds</PropertyName>
  </TableColumnItem> 

If I wanted to have the time in seconds here I could use a script block instead of a property name

   <TableColumnItem> 
    <ScriptBlock>$_.OnTimeInMilliseconds / 1000<ScriptBlock> 
  </TableColumnItem> 

I’m not going pretend that putting the XML together is as quick as writing a list function – but it definitely seems worth it. 

Technorati Tags: Microsoft,Windows,PowerShell,Formatting,XML