Learn about Windows PowerShell
(Note: These solutions were written for Beginner Event 7 of the 2010 Scripting Games.)
Beginner Event 7 (Windows PowerShell)
Sean Kearney is a network administrator, a Microsoft Certified Technology Specialist in Windows Server Virtualization and Configuration, a Windows PowerShell MVP, and a Microsoft Certified Systems Engineer. Sean is a devoted and passionate computer enthusiast from the early 80s to the present day, having used just about every microcomputer ever. Sean taught himself computer programming with 65xxmachine code, working with many technologies―but primarily Microsoft. Sean deals with “anything thrown at him,” from gnawed keyboards to recovery of Exchange servers to networking setups and isolating the realm of the unknown. Currently, he tests and deploys just about any new Microsoft Technology; he also deals with users in an enterprise class environment. Sean loves Windows PowerShell, Windows 7, and Hyper-V, in that order.
Okay, first draft. This is bad because all I did was Bing it and translate the VBScript to Windows PowerShell. But I think it’s important for Windows PowerShell guys to remember that a lot of solutions have already been written in VBScript and sometimes they don’t have to rebuild the wheel. Cue Mission Impossible theme.
AGENT: Sean P. Kearney, Energized Tech, “ye110wbeard”, Lover of Windows Powershell
MISSION: Create a script using only Windows Powershell that can be carried about to easily see what is registered under “Administrative Tools” on a workstation.
This email message will dissolve itself into binary bits and scatter itself back into the days of ENIAC in five seconds.
… I knew I shouldn’t have gotten out of bed this morning. I looked into the screen of my smart phone at those glaring letters in the title.
“The Scripting Guys would like you to be an expert commentator for the Scripting Games 2010!” “Grumble, not more junk e-mail!” I curse to the ceiling. My day never ends.
Like you, I have more tasks in a single day than an old hound has fleas. I don’t have time to sit down, for fires forever have to be put out, people have to be appeased. Bosses must be made happy. But the message was from Microsoft so I read further on to amuse myself.
“We would like you to write a script in Windows Powershell to show all applications registered under Administrative Tools.”
I paused. Don’t these guys do any work? They want me to do their work for them and be a commentator? Must be a particularly difficult task if The Scripting Guys at Microsoft couldn’t handle it.
List those? Aw, c’mon, that‘s too easy. I could have used DOS for that by just doing a directory command on the “Administrative Tools” folder under the Start menu and…then I read on.
List the names. Oh. They wanted the names. The directory command was going to list “Shortcuts” with .lnk extensions which could have been anything. Shoot!
“Better get on Bing.com and start searching,” I figured. I’m an IT pro, not a developer. Must be a program for this.
There was. But it was written in VBScript, used namespaces, involved MSDN references. At this point I was really ready to press the Delete button on the phone and give up on this silly idea. But something hit me. They said in Windows PowerShell.
Translating VBScript to Windows PowerShell is easy but they don’t always use the same terms or refer to the variables in the same way. Here’s the original VBScript code:
Const ADMINISTRATIVE_TOOLS = &H2f&Set objShell = CreateObject("Shell.Application")Set objFolder = objShell.Namespace(ADMINISTRATIVE_TOOLS) Set objTools = objFolder.ItemsFor i = 0 to objTools.Count - 1 Wscript.Echo objTools.Item(i)Next
The First line in VBScript says, “Assign the value 2f in Hexadecimal to the Constant ADMINISTRATIVE_TOOLS”. In Windows PowerShell, we can do that with:
Next, an instance in VBScript is created to the Shell.Application ComObject (Windows Explorer). That same line in Windows PowerShell is:
$objShell=NEW-OBJECT –comobject ‘Shell.Application’
Next, the older script refers to the namespace. It’s very much the same in Windows PowerShell. Again, the difference is in the syntax:
Finally, VBScript pulls up the items within the folder. Again, it’s no different with Windows PowerShell, except the method is called up:
Then VBScript loops through the list to display each item. But in Windows PowerShell, it’s a lot easier. We already have the list. We just want to see the name. So in Windows PowerShell, we just select that object Name:
$objTools | Select-Object Name
But here’s the cool part VBScript can’t do. I can link all of this together in Windows PowerShell as a single line:
((New-Object -comobject Shell.Application).NameSpace(0x2f)).Items() | Select-Object Name
Okay, it would have been cooler to talk all developer about how to interface with namespaces and understand the deeper meanings of objects and life, the universe, and everything.
But I think it’s a lot better to help enable people that are comfortable with VBScript to know a little Windows PowerShell, and conversely for Windows PowerShell people to learn how to translate a little VBScript. Because we don’t need to be gurus to use it. We just need to know how to leverage the resources we have.
Here is the entire Windows PowerShell script:
-------------- LISTTOOLS.PS1 -------------------------## Access Special Folder 0x2f (hexadecimal) "Administrative Tools"#$AdminTools=0x2f## Access ComObjects for Explorer#$ExplorerShell=New-object -comobject Shell.Application## Call up a reference to "Special Folder"#$SpecialFolder=$ExplorerShell.NameSpace($AdminTools)## Access items with "Items()" method, show only "Name" object in output#$SpecialFolder.Items() | Select-Object Name## One-line version#-------------- LISTTOOLS.PS1 ------------------------- “One Liner((New-Object -comobject Shell.Application).NameSpace(0x2f)).Items() | Select-Object Name
Here is the output after running the script:
Beginner Event 7 (VBScript)
Bill Stewart is a scripting guru whose day job is IT systems analyst for French Funeral and Cremation Services in Albuquerque, New Mexico, USA. He has published numerous articles in Windows IT Pro magazine and is a moderator for The Official Scripting Guys Forum!
I’m honored to be asked to be a guest commentator for the 2010 Scripting Games. My first thought regarding this problem was to search the installed applications key (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall) for the installation path to the tools, and then enumerate the files in the installation path. However, this approach wouldn’t give a very user-friendly list of tools.
The Start menu contains such a user-friendly list, of course. It’s easy to enumerate the contents of a folder using a VBScript script; all we need to do is find the location of the Administrative Tools folder in the Start menu folder hierarchy and enumerate the shortcuts in the folder. However, this is not quite completely straightforward. Two complications came to mind:
1. The Administrative Tools folder is not in the same location on every platform. For example, Windows 7 uses the path C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools, while Windows XP uses the path C:\Documents and Settings\All Users\Start Menu\Programs\Administrative Tools. I avoid hard-coding path names unless there is absolutely no alternative, because hard-coded paths tend to make a script more brittle and dependent on specific platforms to work properly.
2. Localization. Non-English versions of Microsoft Windows may use a different folder name than Administrative Tools.
There is a better way to retrieve the shortcuts’ names in the Administative Tools folder: The Shell.Application object’s NameSpace method retrieves a Folder object (distinct from the FileSystemObject object’s Folder object), and the Folder object has an Items method that returns a collection of items in the folder. For example, this short script lists the files and folders in the root directory of drive C:
Dim ShellApp, Folder, ItemSet ShellApp = CreateObject("Shell.Application")Set Folder = ShellApp.NameSpace("C:\")For Each Item In Folder.Items WScript.Echo Item.NameNext
This example uses a string for the NameSpace method’s parameter, but the NameSpace method also supports a set of numeric values that represent special folders on the system. The hexadecimal value 2F represents the “all users” Administrative Tools folder. So all my script needs to do is create the Shell.Application object, use its NameSpace method to retrieve a Folder object that references the Administrative Tools folder, and then enumerate the items in the folder.
First, the script declares a couple of constants to simplify numeric values later in the script. After that, the script defines the ScriptHost function that lets the script detect which script host is executing the script. The script will use this function to abort the script if the script isn’t being executed with CScript.exe. (If the script didn’t do this, the WScript.Echo statement in the loop later in the script would display a series of GUI message boxes.)
The rest of the script is commented and mostly self-explanatory. For style points, I used For instead of For Each to enumerate the shortcuts in the Administrative Tools folder to count the shortcuts. The end of the script uses the N variable to output the number of administrative tools it found. The following image shows an example of running the script on a Windows 7 system.
Here is the complete script:
' ListAdminTools.vbs - written by Bill StewartConst CSIDL_COMMON_ADMINTOOLS = &H2FConst ERROR_FILE_NOT_FOUND = 2' Returns the current script host's name (e.g. "cscript.exe") in lowercase.Function ScriptHost() ScriptHost = LCase(Mid(Wscript.FullName, Len(Wscript.Path) + 2))End Function' End script if not running with CScript.If ScriptHost() <> "cscript.exe" Then WScript.Echo "You must run the script using the CScript host." WScript.Quit 0End IfDim ShellApp, Folder, Items, NSet ShellApp = CreateObject("Shell.Application")Set Folder = ShellApp.NameSpace(CSIDL_COMMON_ADMINTOOLS)' Disable VBScript's default error handler and abort the script' gracefully if the Folder object or its Items method are invalid.' Afterward, enable the default error handler again.On Error Resume NextSet Items = Folder.ItemsIf Err.Number <> 0 Then WScript.Echo "No administrative tools found" WScript.Quit ERROR_FILE_NOT_FOUNDEnd IfOn Error GoTo 0' Iterate the collection of items and output each one.For N = 0 To Items.Count - 1 WScript.Echo Items.Item(N).NameNextIf N > 0 Then WScript.Echo vbNewLine & CStr(N) + " administrative tool(s) found"Else WScript.Echo "No administrative tools found" WScript.Quit ERROR_FILE_NOT_FOUNDEnd If
If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at firstname.lastname@example.org or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson and Craig Liebendorfer, Scripting Guys
I disagree with the powershell solution.
There are 2 main points that were not considered:
- · Style points awarded if you display a summary message that states how many tools are installed.
- For people writing in Windows PowerShell, you must check to see if the script is running inside the Windows PowerShell ISE. If it is, you must exit the ISE (this is an arbitrary requirement that is meant to bring a little parity between VBScript requirements and Windows PowerShell requirements; however, knowing the Windows PowerShell host name is a useful technique in and of itself).