Hey, Scripting Guy! Event 2 *Solutions* from Expert Commentators (Beginner and Advanced; the long jump)

2009 Summer Scripting Games  

(Note: These solutions were written for Event 2.)   

Beginner Event 2: The Long Jump

In the long jump event, you will be asked to determine the speed, the strength, and the agility of your computer.  

Guest commentator: Scott Hanselman

Guest commentator Scott Hanselman

Scott Hanselman is a principal program manager at Microsoft. He maintains the computer zen Web site and can be found on Twitter.

Windows PowerShell solution

At first blush, this event is relatively simple. This is because all of the information can be obtained by using the Get-WmiObject cmdlet and then storing the results into a variable. The more difficult part of the event is cleaning up the output. Here is the 5-minute, one-line solution. The code is not very readable, however:

gwmi win32_processor |fl name,MaxClockSpeed,L2CacheSize, L2CacheSpeed, L3CacheSize, L3CacheSpeed, NumberOfCores,NumberOfLogicalProcessors,AddressWidth

Here is a more involved solution that follows the spirit of the event:

BeginnerEvent2.ps1

set-alias ?: Invoke-Ternary -Option AllScope -Description “PSCX filter alias”
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse)
{
   if (&$decider) {
      &$ifTrue
   } else {
      &$ifFalse
   }
}

$hold = gwmi win32_processor | select Name,MaxClockSpeed,L2CacheSize, L2CacheSpeed, L3CacheSize, L3CacheSpeed, NumberOfCores,NumberOfLogicalProcessors,AddressWidth

write-host -fore “green” Strength Evaluation for (gi ENV:COMPUTERNAME).Value
write-host -fore “yellow” Speed … $hold.MaxClockSpeed
write-host -fore “yellow” L2 Cache Size: (?: {$hold.L2CacheSize} {$hold.L2CacheSize} {“N/A”})
write-host -fore “yellow” L2 Cache Speed: (?: {$hold.L2CacheSpeed} {$hold.L2CacheSpeed} {“N/A”})
write-host -fore “yellow” L3 Cache Size: (?: {$hold.L3CacheSize} {$hold.L3CacheSize} {“N/A”})
write-host -fore “yellow” L3 Cache Speed: (?: {$hold.L3CacheSpeed} {$hold.L2CacheSpeed} {“N/A”})
write-host -fore “magenta” Strength …
write-host -fore “magenta” Number of Cores: $hold.NumberOfCores
write-host -fore “magenta” Number of Logical Processors: $hold.NumberOfLogicalProcessors
write-host -fore “magenta” Name: $hold.Name
write-host -fore “cyan” Agility …
write-host -fore “cyan” Address Width: $hold.AddressWidth

I used the ?: alias from the PowerShell Community Extensions Project (PSCX). You can download the PSCX snap-in from CodePlex. The 1.1.1 version of PSCX for Windows PowerShell 1.0 works great. There is a 1.2 version in beta for Windows PowerShell 2.0. The reason for using the PSCX alias ?: is because there is no Ternary Operator in Windows PowerShell. After I installed the PSCX and had access to the ?: alias, I could do something cool like this:


(?: {$hold.L2CacheSize} {$hold.L2CacheSize} {“N/A”})

When the script is run, this output is displayed:

Image of the output display by the script

 

Guest commentator: Marcus Lerch

Guest commentator Marcus Lerch 

Marcus Lerch is a senior premier field engineer (PFE) for Microsoft Germany. He specializes in Platforms & Active Directory.

The beginner long jump task is to gather some information about the processor in our computer. The best place to retrieve this information is from the processor itself. The easiest way to obtain this information is to use Windows Management Instrumentation (WMI). My complete Windows PowerShell solution for event two is seen here.

BeginnerEvent2_Marcus.ps1

param([string]$Computer = “”)
cls
if($Computer -eq “”){$Computer = get-content env:COMPUTERNAME}

$proc = Get-WmiObject win32_processor -computername $Computer

write-host “Strength evaluation for” $Computer -foregroundcolor green
write-host “Speed …” -foregroundcolor yellow
if ($proc.MaxClockSpeed -ne $NULL)
    {$out = $proc.MaxClockSpeed}
else
    {$out = “unable to determine”}
write-host “MaxClockSpeed               :” $out -foregroundcolor yellow
if ($proc.L2CacheSize -ne $NULL)
    {$out = $proc.L2CacheSize}
else
    {$out = “unable to determine”}
write-host “L2CacheSize                 :”  $out -foregroundcolor yellow
if ($proc.L2CacheSpeed -ne $NULL)
    {$out = $proc.L2CacheSpeed}
else
    {$out = “unable to determine”}
write-host “L2CacheSpeed                :” $out -foregroundcolor yellow
if ($proc.L3CacheSize -ne $NULL)
    {$out = $proc.L3CacheSize}
else
    {$out = “unable to determine”}
write-host “L3CacheSize                 :”  $out -foregroundcolor yellow
if ($proc.L3CacheSpeed -ne $NULL)
    {$out = $proc.L3CacheSpeed}
else
    {$out = “unable to determine”}
write-host “L3CacheSpeed                :”  $out -foregroundcolor yellow
write-host “Strength …” -foregroundcolor magenta
if ($proc.NumberOfCores -ne $NULL)
    {$out = $proc.NumberOfCores}
else
    {$out = “unable to determine”}
write-host “NumberOfCores               :” $out -foregroundcolor magenta
if ($proc.NumberOfLogicalProcessors -ne $NULL)
    {$out = $proc.NumberOfLogicalProcessors}
else
    {$out = “unable to determine”}
write-host “NumberOfLogicalProcessors   :” $out -foregroundcolor magenta
if ($proc.Name -ne $NULL)
    {$out = $proc.Name}
else
    {$out = “unable to determine”}
write-host “Name                        :” $out -foregroundcolor magenta
write-host “Agility …” -foregroundcolor cyan
if ($proc.AddressWidth -ne $NULL)
    {$out = $proc.AddressWidth}
else
    {$out = “unable to determine”}
write-host “AddressWidth                :” $out -foregroundcolor cyan

To get the WMI information, use the Get-WmiObject cmdlet and point it toward the Win32_processor WMI class, andbingo!you get all the information you wanted to see (and a lot more).

All you have to do now is read the values you need and present them to the user. So we assign the object the name $proc and use this object to get the information needed. This is seen here:

$proc = Get-WmiObject win32_processor -computername $Computer

The next step is to check whether we could get the information via WMI. For example, we determine whether the attribute of the object is not equal to $NULL. If the attribute is null, which means either there is no value in that attribute or we could not read it, we tell the user that we were unable to determine the information.

Now just display what we found on the screen (using some different colors for the output to make it look good).

As a little extension, I included the option to call the script with a computer name to connect to a remote computer instead of using the local computer. If no computer name is given to the command-line parameter, I use the environment variable COMPUTERNAME to connect to the local computer. Here is what it looks like when the script runs:

Image of the script's product  

Guest commentator: Ed Wilson

Image of guest commentator Ed Wilson

 

Ed Wilson is one of the Microsoft Scripting Guys and a well-known scripting expert. He is a Microsoft-certified trainer who has delivered a popular Windows PowerShell workshop to Microsoft Premier Customers worldwide. He has written eight books, five on the subject of Windows scripting, including Windows PowerShell Scripting Guide and Microsoft Windows PowerShell Step by Step, both published by Microsoft Press. His WMI book, Microsoft Windows Scripting with WMI: Self-Paced Learning Guide, is the best-selling WMI book on the market. He has also contributed to nearly a dozen other books and is currently working on a PowerShell Best Practices book.

Ed holds more than 20 industry certifications, including Microsoft Certified Systems Engineer (MCSE) and Certified Information Systems Security Professional (CISSP). Before coming to work for Microsoft, he was a senior consultant for a Microsoft Gold Certified Partner, where he specialized in Active Directory design and Exchange implementation.

 

VBScript solution

The beginner long jump event required using WMI to retrieve specific pieces of information about the processor on the computer. To do this, you can use WMI. In VBScript, the best way to query WMI is to use the GetObject command and pass it a WMI moniker that points to the computer and the WMI namespace. These two pieces of informationcomputer name and WMI namespaceare actually optional, but if you always include them, you can create a WMI template script that will greatly reduce the amount of time spent typing redundant information. Here is the WMI template that I use for creating quick WMI scripts:

WMITemplate.vbs

‘==========================================================================


‘ NAME: <wmiTemplate.vbs>

‘ AUTHOR: Ed Wilson , MS
‘ DATE  : $DATE

‘ COMMENT: <Use as a WMI Template>

‘==========================================================================

Option Explicit
On Error Resume Next
dim strComputer      ‘target computer
dim wmiNS   ‘target wmi name space
dim wmiQuery         ‘the WMI query
dim objWMIService ‘sWbemservices object
dim colItems         ‘sWbemObjectSet object
dim objItem          ‘sWbemObject

Const RtnImmedFwdOnly = &h30 ‘iflags for ExecQuery method of swbemservices object
strComputer = “.”
wmiNS = “rootcimv2”
wmiQuery = “Select * from win32_”

Set objWMIService = GetObject(“winmgmts:\” & strComputer & wmiNS)
Set colItems = objWMIService.ExecQuery(wmiQuery,,RtnImmedFwdOnly)

For Each objItem in colItems
    Wscript.Echo “: ” & objItem.
    Wscript.Echo “: ” & objItem.
    Wscript.Echo “: ” & objItem.
    Wscript.Echo “: ” & objItem.
    Wscript.Echo “: ” & objItem.
    Wscript.Echo “: ” & objItem.
Next

In the header section of the script, we declare all of the variables and specify both Option Explicit and On Error Resume. Next, we move into the reference section of the script. (This script follows the four-part scripting model that was created in the Microsoft Press book, Microsoft Windows Scripting Self-Paced Learning Guide.)

A constant, RtnImmedFwdOnly, is created and set to hexadecimal 30. This is equivalent to decimal 48. The iflags parameter is documented on MSDN and is used to control the way the script runs. Hexadecimal 20 means to forward only cursor, and Hexadecimal 10 means to return immediately. When added together, we get Hexadecimal 30, which means forward only and return immediately. This value, when supplied to the ExecQuery method, will greatly improve the performance of the WMI query.

The WMI moniker is used with the GetObject command. It is seen here:

Set objWMIService = GetObject(“winmgmts:\” & strComputer & wmiNS)

A WMI service object is created and stored in the objWMIService variable. This WMI service object is used to execute the query that we stored in the wmiQuery variable. The object that is returned is a collection of Win32_Processor objects. (This happens even if you only have a single processor. The ExecQuery method always returns a collection. So in VBScript, you will always use For..Each…Next). This is shown here:

Set colItems = objWMIService.ExecQuery(wmiQuery,,RtnImmedFwdOnly)

We now use the For…Each…Next construction to walk through the collection of WMI objects that is stored in the colItems variable. Inside the For…Each…Next construction, we use the Wscript.Echo command to display information about the processor. If the script is run in cscript, as seen here, the results will be output to the command console. (For more information about running scripts, refer to the Learning section of the Script Center.)

Cscript BeginnerEvent2.vbs

If you just double-click the script, each value will be displayed in a pop-up box. The completed BeginnerEvent2.vbs script is seen here:

BeginnerEvent2.vbs

‘==========================================================================


‘ NAME: Beg_2.vbs

‘ AUTHOR: Ed Wilson , MS
‘ DATE  : 6/2/2009

‘ COMMENT: Summer Scripting Games 2009, Long Jump event.

‘==========================================================================

Option Explicit
On Error Resume Next
dim strComputer      ‘target computer
dim wmiNS   ‘target wmi name space
dim wmiQuery         ‘the WMI query
dim objWMIService ‘sWbemservices object
dim colItems         ‘sWbemObjectSet object
dim objItem          ‘sWbemObject

Const RtnImmedFwdOnly = &h30 ‘iflags for ExecQuery method of swbemservices object
strComputer = “.”
wmiNS = “rootcimv2”
wmiQuery = “Select * from win32_processor”
Set objWMIService = GetObject(“winmgmts:\” & strComputer & wmiNS)
Set colItems = objWMIService.ExecQuery(wmiQuery,,RtnImmedFwdOnly)

For Each objItem in colItems
    Wscript.Echo “AddressWidth: ” & objItem.AddressWidth
    Wscript.Echo “L2CacheSize: ” & objItem.L2CacheSize
    Wscript.Echo “L2CacheSpeed: ” & objItem.L2CacheSpeed
    Wscript.Echo “L3CacheSize: ” & objItem.L3CacheSize
    Wscript.Echo “L3CacheSpeed: ” & objItem.L3CacheSpeed
    Wscript.Echo “MaxClockSpeed: ” & objItem.MaxClockSpeed
    Wscript.Echo “Name: ” & objItem.Name
    Wscript.Echo “NumberOfCores: ” & objItem.NumberOfCores
    Wscript.Echo “NumberOfLogicalProcessors: ” & objItem.NumberOfLogicalProcessors
Next

 

Advanced Event 2: The Long Jump

The long jump is an event in which athletes combine speed, strength, and agility in an attempt to leap as far as possible from a takeoff point. In this event, you will be required to rearrange an Office Excel spreadsheet containing the results of jumpers as well as their expected distances. Who is meeting expectations, exceeding, or falling short in their performance? 

Guest commentator: Chris Bellée

Chris Bellee is a premier field engineer for Microsoft. He is based in Sydney, Australia. He teaches a very popular Windows PowerShell class to Microsoft Premier customers.

Windows PowerShell solution

In my script, I used the 2007 Office System Driver: Data Connectivity Components (ACE components). This is not a standard installation, but you can download the ACE components from the Microsoft Download Center. The advantage of using these components is the .NET Framework classes can then be used to interact with non-Microsoft applications as well. The ACE components are discussed on MSDN. The object model is pretty easy to work with. I have been doing a lot of work recently that required me to use these components. The other thing you need to know about this script is that if you are using a 64-bit version of Microsoft Windows and you use the 64-bit version of Windows PowerShell, you will get an error. The error is misleading, stating, “The provider is not registered on the local machine.” The error message is seen here:

Image of the error message

To work around the 64-bit/32-bit problem, launch the 32-bit version of Windows PowerShell on your 64-bit operating system. You will find the 32-bit of Windows PowerShell in the program folder along with the 64-bit version. The 32-bit Windows PowerShell will have “(x86)” after its name. Here is the complete AdvancedEvent2.ps1 script for the 2009 Summer Scripting Games 2009 advanced long jump event.

AdvancedEvent2.ps1


####################################################

Function Get-ExcelData {

Param([string]$strFilePath=”./LongJump_Adv2.xls”,$strSheetname=”Jump Data”)

$strSheetname=$strSheetname+’$’
$strFilepath=resolve-path $strFilePath
$strConn=”Provider=Microsoft.ACE.OLEDB.12.0;Data Source=$strfilePath;Extended Properties=’Excel 12.0;HDR=Yes'”
$objXlsConn = New-Object system.data.oledb.oledbconnection($strConn)
$objXlsConn.open()

$objCmd = New-Object system.data.oledb.oleDbCommand(“select * from [$strSheetname]”,$objXlsConn)
$objDa = New-Object system.data.oledb.oleDbDataAdapter($objCmd)
$objDs = New-Object system.data.DataSet

[void]$objDa.fill($objDs, “UserData”)
$objXlsConn.close()

$colNames = ($objDs.tables[“UserData”].columns) | %{$_.columnName}

$objDs.tables[“UserData”].rows | foreach {
          $objRow = $_
          $objPS = new-object psobject

          $colNames | foreach-object {
                   $colName = $_
            &