Hey, Scripting Guy! Event 6 *Solutions* from Expert Commentators (Beginner and Advanced; the 110-meter hurdles)


  

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

Beginner Event 6: The 110-meters hurdles

In the 110-meter hurdles, you have to overcome a number hurdles as you attempt to make your script run. Debugging skills are useful for this event. 

Guest commentator: Uros Calakovic

Image of guest commentator Uros Calakovic

Uros Calakovic is a system and database administrator from Bijeljina, Bosnia and Herzegovina. He is also a moderator for the Official Scripting Guys Forum, and has written articles for the Code Project.


VBScript solution

The 110-meter hurdles Beginner Event 6 puts me in just the kind of a situation that I try to avoid when writing a script. The script contains multiple errors hidden by the On Error Resume Next statement at the beginning. If this were a “real life” script, the scripter most likely assembled it in one piece without testing the script once. I avoid these kinds of problems by frequent testing. As a best practice, I run the script each time I add a new piece of executable code. I check for errors during each subsequent running of the script.

When faced with a script like this (mine or somebody else’s), my strategy for troubleshooting purposes is similar. If there is an On Error Resume Next statement at the beginning, I remove it, run the script with cscript.exe (from the command line), and resolve the errors one by one as they appear at the command prompt.

For this particular script, I first need to figure out what the script should do from the code itself and the enclosed picture. The script, as the picture suggests, should create three shortcuts on the user’s desktop. The script fails silently, so I follow the usual procedure, and the first error after removing the On Error Resume Next and running the script I get is this:

C:scriptsbeg_6.vbs(18, 1) Microsoft VBScript runtime error: Variable is undefined: ‘objShell’

The first statement in the script is Option Explicit, which means that you must declare every variable used in the script. This is considered a good practice, especially if it is a long script, because it prevents you from misspelling a variable name. This is exactly what caused this error: I check the list of declared variables and there is a variable named oShell which is never used in the script, so I change the variable name to objShell. I run the script again and the next error appears:

C:scriptsbeg_6.vbs(28, 1) WshShortcut.Save: Unable to save shortcut “C:Documents and SettingsUrkecDesktop Shortcut Script.lnk”.

This error usually occurs when a read-only shortcut with the same path as the one you are trying to create already existsbut not in this case. It takes me a minute to spot what causes the error message.  The above path contains an extra space after Desktop, so I correct the line to:

set objShortCut = objShell.CreateShortcut(strDesktop & “Shortcut Script.lnk”)

I run the script again and two things happen: The first shortcut appears on the desktop, but its icon does not look like the Notepad icon. I open the shortcut’s Properties, click the Change Icon… button, and the dialog offers only one icon for Notepad.exe. Its index is probably zero (0), so I assign the icon to the first shortcut:

objShortCut.IconLocation = “notepad.exe, 0”

At the same time, the first shortcut appears and I receive this error:

C:scriptsbeg_6.vbs(30, 1) Microsoft VBScript runtime error: Variable is undefined: ‘wshShell’

The variable named WshShell is used only once in the script to create the WshShell object, which creates the second shortcut. I could add Dim WshShell to the declaration list and the script would work fine, but I decide this is a typo and change the line to:

Set objShell = CreateObject(“Wscript.Shell”)

This reveals the next error:

C:scriptsbeg_6.vbs(33, 1) Microsoft VBScript runtime error: Object doesn’t support this property or method: ‘Discription’

At first this looks like just another typo so I change the line to the following code:

objURL.Description = “Scripting Guys”

But, it appears that this is not just another misspelled word, because after running the script again, I get the same error:

C:scriptsbeg_6.vbs(33, 1) Microsoft VBScript runtime error: Object doesn’t support this property or method: ‘Description’

This means that objURL really doesn’t have a member named Description, which is strange because the Description property is successfully used while creating the first shortcut. I temporarily add this line to the script:

WScript.Echo TypeName(objURL)

This checks the objURL type, and the type is:

IWshURLShortcut

I then check the documentation, and the only members for WshURLShortcut are FullName, TargetPath, and Save(), which means that you can’t set a description for Internet shortcuts and the line must be deleted. Also, just as with the first shortcut, the second shortcut must be saved in order to appear on the desktop, so I add this line:

objURL.Save()

After this change, the first two shortcuts are created, and the next error shown is:

C:scriptsbeg_6.vbs(35, 1) Microsoft VBScript runtime error: Variable is undefined: ‘wshNetwork’

This variable holds the WScript.Network object never used in the script, but I still add this line to the declaration section of the script:

Dim wshNetwork

After that I run the script and the next error is:

C:scriptsbeg_6.vbs(38, 1) WshShell.CreateShortcut: The shortcut pathname must end with .lnk or .url.

This is easy, and I change the line to:

set objShortCut = objShell.CreateShortcut(strDesktop & “notepad.lnk”)

The last error is:

C:scriptsbeg_6.vbs(40, 1) Microsoft VBScript runtime error: Invalid procedure call or argument

The documentation says WshShortcut.TargetPath must be the path to the executable, but I just change notpad.exe to notepad.exe and the script finally seems to be error-free. Three working shortcuts are now created on my desktop.

This is what the script looks like after the above session:

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

‘ VBScript:  AUTHOR: Ed Wilson , msft,  5/8/2009

‘ NAME: Beg_6.vbs

‘ problem script for Beginner event 6.
‘ Summer Scripting Games 2009
‘=================================================================
Option Explicit
On Error Resume Next
Dim objShell ‘Instance of the WshShell object
Dim strDesktop ‘Pointer to desktop special folder
Dim objShortCut ‘Used to set properties of the shortcut. Comes from using CreateShortcut
Dim objURL ‘Used to set properties of webshortcut.
Dim objNotepad
Dim wshNetwork

set objShell = CreateObject(“WScript.Shell”)
strDesktop = objShell.SpecialFolders(“Desktop”)

set objShortCut = objShell.CreateShortcut(strDesktop & “Shortcut Script.lnk”)
objShortCut.TargetPath = WScript.ScriptFullName
objShortCut.WindowStyle = 0
objShortCut.Hotkey = “CTRL+SHIFT+F”
objShortCut.IconLocation = “notepad.exe, 0”
objShortCut.Description = “Shortcut Script”
objShortCut.WorkingDirectory = strDesktop
objShortCut.Save()

Set objShell = CreateObject(“Wscript.Shell”)
set objURL = objShell.CreateShortcut(strDesktop & “The Microsoft Scripting Guys.url”)
objURL.TargetPath = “http://www.ScriptingGuys.com”
objURL.Save()

Set wshNetwork = CreateObject(“WScript.Network”)
set objShortCut = objShell.CreateShortcut(strDesktop & “notepad.lnk”)
objShortCut.TargetPath = “notepad.exe”
objShortCut.IconLocation = “notepad.exe, 0”
objShortCut.description = “notepad”
objShortCut.Save

 

The script now works fine, but there is still some room for improvement. I can make some changes without affecting the script’s functionality. First, I will make some cosmetic changes. I capitalize all VBScript keywords, add a couple of empty lines, and so on.

Next, I remove all unnecessary parts. The objNotepad variable is declared, but is never used in the script. WshNetwork is declared and a WScript.Network instance is created, but you don’t need this object to create desktop shortcuts. And a WScript.Shell instance is created twice.

The script now looks like this:

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

‘ VBScript:  AUTHOR: Ed Wilson , msft,  5/8/2009

‘ NAME: Beg_6.vbs

‘ problem script for Beginner event 6.
‘ Summer Scripting Games 2009
‘ This script does not work. It needs to be fixed.
‘==========================================================================

Option Explicit
On Error Resume Next
Dim objShell ‘ Instance of the WshShell object
Dim strDesktop ‘ Pointer to desktop special folder
Dim objShortcut ‘ Used to set properties of the shortcut.
Dim objURL ‘ Used to set properties of webshortcut.

Set objShell = CreateObject(“WScript.Shell”)
strDesktop = objShell.SpecialFolders(“Desktop”)

‘ Create the first shortcut

Set objShortcut = objShell.CreateShortcut(strDesktop & _
    “Shortcut Script.lnk”)

objShortcut.TargetPath = WScript.ScriptFullName
objShortcut.WindowStyle = 0
objShortcut.Hotkey = “CTRL+SHIFT+F”
objShortcut.IconLocation = “notepad.exe, 0”
objShortcut.Description = “Shortcut Script”
objShortcut.WorkingDirectory = strDesktop
objShortcut.Save()

‘ Create the second shortcut

Set objURL = objShell.CreateShortcut(strDesktop & _
    “The Microsoft Scripting Guys.url”)

objURL.TargetPath = “http://www.ScriptingGuys.com”
objURL.Save()

‘ Create the third shortcut

Set objShortcut = objShell.CreateShortcut(strDesktop & _
          “notepad.lnk”)

objShortcut.TargetPath = “notepad.exe”
objShortcut.IconLocation = “notepad.exe, 0”
objShortcut.description = “notepad”
objShortcut.Save()

A script like this is fine if you don’t have to create shortcuts often. But if you do, constantly repeating the same task can quickly become tedious. If you look at the script, you can see that the same stepscall WshShell.CreateShortcut(), assign its properties, save the shortcutare performed for each shortcut. This type of code is a good candidate for a function (or a subroutine). By using functions for repetitious tasks, you can save yourself a lot of typing. The other advantage is, if you put a piece of code in a function and test it thoroughly, you can use the function as a black box and don’t have to think about the code logic again. Here is how the script could look like when a subroutine is used for creating a shortcut:


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

‘ VBScript:  AUTHOR: Ed Wilson , msft,  5/8/2009

‘ NAME: Beg_6.vbs

‘ problem script for Beginner event 6.
‘ Summer Scripting Games 2009

‘ Uros Calakovic, 5/16/2009
‘ use a function to avoid repeating the task
 
‘==========================================================================

Option Explicit

Dim objShell ‘ Instance of the WshShell object
Dim strDesktop ‘ Pointer to desktop special folder

Set objShell = CreateObject(“WScript.Shell”)
strDesktop = objShell.SpecialFolders(“Desktop”)

AddShortcut strDesktop & “Shortcut Script.lnk”, WScript.ScriptFullName, _
    0, “CTRL+SHIFT+F”, “Notepad.exe, 0”, “Shortcut Script”, strDesktop

AddShortcut strDesktop & “The Microsoft Scripting Guys.url”, _
    “http://www.ScriptingGuys.com”, Null, Null, Null, Null, Null


AddShortcut strDesktop & “notepad.lnk”, “notepad.exe”, 651, _
    Null, “notepad.exe, 0”, “notepad”, Null


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

‘ Creates a shortcut

‘ Input parameters:
     strShortcutPath     – path to the shortcut including its name and extension
     strTargetPath       – the path to the shortcut’s executable
     intWindowStyle      – the window style for the shortcut
     strHotkey           – the key combination to the shortcut
     strIconLocation     – the icon location for the shortcut
     strDescription      – shortcut’s description
     strWorkingDirectory – working directory for the shortcut     

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

Sub AddShortcut(strShortcutPath, strTargetPath, intWindowStyle, _
    strHotkey, strIconLocation, strDescription, strWorkingDirectory)

    Dim objWshShell
    Dim objShortcut

    Set objWshShell = CreateObject(“WScript.Shell”)
    Set objShortcut = objWshShell.CreateShortcut(strShortcutPath)
    objShortcut.TargetPath = strTargetPath

    If TypeName(objShortcut) = “IWshShortcut” Then

        If Not IsNull(intWindowStyle) Then
            objShortcut.WindowStyle = intWindowstyle
        End if

        If Not IsNull(strHotKey) Then
            objShortcut.Hotkey = strHotkey
        End If

        If Not IsNull(strIconLocation) Then
            objShortcut.IconLocation = strIconLocation
        End If

        If Not IsNull(strDescription) Then
            objShortcut.Description = strDescription
        End If

        If Not IsNull(strWorkingDirectory) Then
            objShortcut.WorkingDirectory = strWorkingDirectory
        End If

    End If

    objShortcut.Save()

End Sub

When creating a shortcut, you don’t have to assign all of the WshShortcut properties. In the above function, the WshShortcut properties are passed as input parameters. VBscript doesn’t support optional parameters, so I had to simulate this by using the Null keyword. If Null is passed for a parameter, the property doesn’t get assigned. I also decided to use the same procedure for creating both types of shortcuts, which forced me to test for the object type created by WshShell.CreateShortcut() function and assign the properties accordingly. The procedure could be further refined by checking if strTargetPath and strWorkingDirectory exist, checking if intWindowStyle falls within the allowed range from the WshWindowStyle enumeration, etc.

 

Guest commentator: Jeffery Hicks

Image of guest commentator Jeffery Hicks

Jeffery Hicks is an MCSE, MCSA, MCT, and Microsoft PowerShell MVP. He is a scripting guru for SAPIEN Technologies. Jeff is a 17-year IT veteran. He has co-authored and authored several books, courseware, and training videos on administrative scripting and automation. His latest book is Managing Active Directory with Windows PowerShell: TFM (SAPIEN Press 2008). Jeff also writes the Mr. Roboto column for REDMOND Magazine, Professor PowerShell for MCPMag.com and Practical PowerShell for RealTime Publishers. Visit http://blog.sapien.com for Jeff’s latest work and you can follow Jeff on Twitter at http://www.twitter.com/JeffHicks.


Windows PowerShell solution

The first troubleshooting step is to see which errors are occurring so I first comment out this line:

$ErrorActionPreference = “SilentlyContinue”

The script includes a line to use the interactive debugging cmdlet, which I could have modified:

Set-PSDebug -Strict -Step -Trace 1

Personally, I find this to be more complicated to understand, especially for Windows PowerShell beginners. I prefer to insert test commands into my script to check variables and objects while the script is running. You can either comment out or remove this line from your script. Now let’s see which errors I have to deal with.

Running the script results in an error about a missing “)” on line 33. Loading the script into a script editor like PrimalScript that displays the line numbers makes it easier troubleshoot. Line 33 doesn’t require any parentheses. In Windows PowerShell, sometimes the code causing the error happens before the line that generates the error. In looking at the previous line, the “&” character is not used to concatenate as it is in VBScript. My scripting editor includes syntax checking and also indicates something is wrong with this line. I change the “&” to a “+” and run the script again.

Now I get an error that the variable $objShell cannot be retrieved because it was not set on line 20. Windows PowerShell is trying to use an object called $objShell. Where did that come from? Working backward from this point, I can’t find any Windows PowerShell commands that create this object. From my VBScript experience, I know that the CreateShortcut method is part of the WshShell object. Reading through the code I find:

$oShell = New-Object -ComObject (“WScript.Shell”)

This is not the same object. So I change the line to:

$objShell = New-Object -ComObject (“WScript.Shell”)

And try again. Remember to only change one thing at a time and then test. There are still several problems, but I’ll tackle them in order one at a time.

The first problem is that Windows PowerShell is displaying information about a Save method. If you call an object’s method without using (), this is the type of thing you’ll see. I look for the first Windows PowerShell line that is calling the Save method and find:

$objShortCut.Save

It should be:

$objShortCut.Save()

The next error is about a Discription property that can’t be found:

$objURL.Discription = “Scripting Guys”

More than likely, that is not the right property name. It could be Description. But I must confirm my assumption. I can pipe the $objURL object to Get-Member with a few lines of code as seen here:

$objShell = New-Object -ComObject (“WScript.Shell”)
$strDesktop = $objShell.SpecialFolders.item(“Desktop”)
$objURL = $objShell.CreateShortcut($strDesktop + “The Microsoft Scripting Guys.url”)
$objURL | get-member

Looking at the output I discover th

Comments (2)

  1. Anonymous says:

    All the Scripting Games links in one location! Let the learning begin. (We will update this page every

  2. Anonymous says:

    My solution for Advanced event 6 of the Summer Scripting Games is posted at the Script Center : Hey,