Summary: Microsoft PFE, Chris Wu, talks about using Windows PowerShell to work with the Windows Desktop through the Windows Explorer interface.
Microsoft Scripting Guy, Ed Wilson, is here. Today I have the privilege of welcoming back Chris Wu. Chris is a Microsoft premier field engineer (PFE) in Canada. If you are interested, see his other Hey, Scripting Guy! guest blogs. Now, here is Chris…
While playing with Windows PowerShell scripts, one of my favorite COM objects has been Shell.Application. I guess it is because I support Windows operating systems, and Windows Explorer is the primary way we interact with the system. Being able to script many of those tasks with ease is plainly awesome.
Many of our readers are probably familiar with the objects. Some may have even come across a script such as Pin and Unpin Applications from the Taskbar and Start Menu, or something similar, which utilizes this object to pin or unpin an application to the Taskbar or Start Menu.
The script basically does it in a few steps:
- Use the Verbs() method of a FolderItem object to get a list of verbs applicable to the target.
- Search the verbs to find one with a name matching the expected action.
- Execute the DoIt() method to invoke the verb.
The beauty of this technique is that the Verbs() method can return almost all actions found in the right-click context menu. If we compare the list of verbs to the context menu in Windows Explorer, we will find that the two lists are identical (and in the same order)—except for a few items that have submenus.
Although presented in uniform, the items in the list are actually implemented in various ways—as explained in Choosing a Static or Dynamic Shortcut Menu Method. Some of them are static verbs, others dynamic; some implement the IExecuteCommand interface, and some IContextMenu. Depending on the implementation, not all of these items can be called from Windows PowerShell. None-the-less, the previous pattern can be used to try to invoke the verbs without having to understand the implementation.
However, I wanted to invoke a verb in a more direct way to avoid pipeline processing. A little research on MSDN, and I found an InvokeVerb() method of a FolderItem object, which is described as “equivalent to selecting a command from an item’s shortcut menu.” More interestingly the Shell.ShellExecute() method has the vOperation parameter which is “set to one of the verb strings that is supported by the file.”
It seems that both of these methods can invoke a command directly. Unfortunately, they expect “verb strings,” which are language neutral strings that are different from the display names (they usually come from string resources embedded in localized DLL files), and it’s nontrivial to find some of the verb strings.
Although a large number of callable verb strings are registered with file association in HKEY_CLASSES_ROOT under the shell branch, some of them are hidden in the sea of CLSID registry keys. After a lot poking around and testing, following are several verbs that I’ve found useful.
TaskbarPin and TaskbarUnpin
Pin and unpin items to the Taskbar. These verbs are registered as a context menu handler for all items (but applicable only to some file types), and they can be called ShellExecute()—however, not with InvokeVerb.
$o.Namespace("c:\windows\system32").ParseName("rsop.msc").InvokeVerb("taskbarpin")
$o.Namespace("c:\windows").ParseName("regedit.exe").InvokeVerb("taskbarunpin")
RunAs and RunAsUser
Run application elevated or as a different user. These are registered as static verbs for .exe, .bat, and .cmd files. RunAs is additionally available for .cpl files. RunAs works with ShellExecute() or with InvokeVerb(), but RunAsUser works only with ShellExecute().
$o.Namespace("c:\windows\system32").ParseName("Firewall.cpl").InvokeVerb("runas")
$o.ShellExecute("powershell.exe","-command ""get-date""","","runas","")
$o.ShellExecute("c:\temp\test.bat","","","runasuser","")
PinToStartScreen
Pin programs or folders to the start screen in Windows 8. This verb is registered as static verb for .exe files and folders, and it works with ShellExecute() or with InvokeVerb().
$o.Namespace("c:\windows").Self.InvokeVerb("pintostartscreen")
$o.ShellExecute("iexplore.exe","","","pintostartscreen","")
Mount
Mount .iso files in Windows 8 or Windows 7, or mount .vhd files in Windows 8. Mount is registered as static verb for .vhd and .iso files, and it works with ShellExecute() or InvokeVerb().
$o.ShellExecute("C:\Downloads\Win7.iso","","","mount","")
$o.Namespace("C:\Downloads").ParseName("iScsi.vhd").InvokeVerb("mount")
Eject
Eject a removable drive. Honestly, I don’t know how this verb is registered, but it works with InvokeVerb().
$o.Namespace("D:\").Self.InvokeVerb("eject")
Change-Pin, Manage-BDE
Change the PIN or manage BitLocker for an encrypted drive. These are registered as static verbs for drives, and they can be invoked with ShellExecute() or InvokeVerb().
$o.ShellExecute("C:\","","","manage-bde","")
$o.Namespace("D:\").Self.InvokeVerb("change-pin")
cmd
Open a command window here for folders (in Windows 8 or Windows 7). Registered as a static verb for directories, this is an extended verb that doesn’t show in the context menu unless Shift key is held together with right-clicking. This verb works with ShellExecute()only.
$o.ShellExecute("C:\Program Files (x86)","","","cmd","")
Because the list of verbs is dependent on item type (for example, file vs. folder, or the extensions .exe vs. .pdf) and applications installed on the computer, I am sure our readers will find more interesting verbs on their system. And before anybody points it out, I admit that there may be better ways to achieve some of the previous tasks. Taking the last one as an example, we can simply start a cmd.exe process and set the working directory to the target folder. But still, isn’t it sometimes cool to show off a seemingly confusing command that just works?
~Chris
Awesome job, Chris! Thank you for taking the time to share this with us today. Join me tomorrow when I have another guest blog from Chris. Tomorrow he will talk about improving the performance of arrays—you do not want to miss that.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy