Extending Your Options: The Folder Options Control Panel Item

Office Space

Welcome to the first edition of Scripting Eye for the GUI Guy. We’d like to hear from you. What do you think about the premise of the column – looking at a GUI tool and figuring out how to script the same functionality? Which GUI tools would you like to see us cover in future editions? Email us at scripter@microsoft.com (in English, if possible) and let us know. We can’t promise to respond to all of the requests we receive, but we’ll do our best.

On This Page

Extending Your Options
First Things First: Choosing an Enabler
Scripting the GUI

Extending Your Options

The Scripting Guys have a vested interest in convincing you to learn to script. Our jobs pretty much depend on it. So you’ll often hear us say things like, “don’t worry, scripting’s not hard at all” or “you’ll be an expert before you know it.” We do want those statements to be true and we work hard to make them true. But the fact remains that becoming a competent scripter involves learning a number of relatively involved technologies – such as WMI, COM, and ADSI – on top of having expertise in the area of the particular administrative task you are trying to accomplish. So there you have it. We admit it. Learning scripting is not without challenges.

One of the challenges we hear most often is that it’s really difficult to know where to start. We thought long and hard about how to solve that problem. One solution is to provide you with the Scripting Guys’ tried and tested “Dora the Explorer” style roadmap to scripting enlightenment. Sounds nice (and got Greg all excited about the neat glossy posters we could make for you), but the problem is that the roadmap really needs to be customized to what each of you are trying to accomplish with your scripts. If you work at an ISP and spend the majority of your time tuning IIS, then your ideal roadmap is going to look different than the roadmap for someone who does Sarbannes-Oaxley compliance consulting.

So, we thought a bit more. It hurt, but hey, anything for our customers. This column represents a component of our solution. We figured that most of you are very familiar with the GUI tools Windows provides in your area of expertise, so it would be useful to build up a library of documentation that addressed how to script tasks that you can do with various GUI tools. After we build up a large enough library of content, perhaps we can provide you with a simulated Windows interface where navigating to a GUI tool and clicking on it takes you to the corresponding documentation about how to script what that GUI enables you to do. Honest to goodness irony - using a graphical interface as an index into scripting documentation.

Without further ado, let’s kick things off by having a look at a Control Panel item – the one that lets you manage Folder Options. Here’s a screenshot of the item, opened to the File Types tab on which we’re going to focus our scripting eye.

Folder Options Dialog

As you’re all well aware, the Registered file types list box displays all of the known file extensions on the machine along with a brief description of the corresponding file type. Folder Options provides a nice interface to let you view and manage this information. Unfortunately, the GUI doesn’t appear to provide a way to manage this information on a remote machine, let alone a set of remote machines. We can solve this problem by writing a script that gives us the same information that the GUI provides in the list box, but in a way that allows us to run on remote machines.

First Things First: Choosing an Enabler

So, how do you proceed? Where do you find the information you need to get you started writing this script? The first thing you should think about is what enabler can you leverage to get this work done? Of course, you’ll first need to know what I mean by enabler. A scripting language itself, take VBScript for example, isn’t really in the business of doing work. Rather, it provides a way to describe what work should be done, by what workers and in what order. It plays more of a foreman’s role than a worker’s role. When it comes to the actual workers, or enablers, you have a number to choose from in the Windows world: WMI, ADSI, other scriptable COM-based libraries, and command-line tools. If the task were something that clearly involved data manipulation in Active Directory, then ADSI would likely be your best choice of enabler. Otherwise, focus in on WMI and try to find a WMI class that exposes the properties and/or methods you need to script your task.

Obviously our problem doesn’t involve Active Directory, so you’ll want to try WMI. Now that you’ve decided on your enabler, you need to search for the tools within that enabler that will provide the functionality you’re looking for. So fire up the Scriptomatic or wbemtest.exe or whatever WMI repository exploration tool you prefer and start the search. This isn’t an exact science. It’s a similar process to using your favorite search engine to search the Web for information: cross your fingers and hope for a close match. I find the Scriptomatic useful for this searching phase. I scan the WMI class pulldown for likely candidates, select them, run the corresponding script and quickly parse the resulting output to see if there’s any evidence that this might be the right class for my task. In this case, with registered file types, I didn’t run across anything that looked useful. Just like Web search results, that doesn’t mean it’s not there. It just means that I couldn’t find it in a reasonable amount of time.

So what do you do if you’re not working on an Active Directory-related task and WMI doesn’t appear to provide a way to get your task done? You move on to the other enablers: other scriptable COM-based libraries and command-line tools. Searching for appropriate COM-based libraries is not an easy task for a system administration scripter. You can use tools such as OLEView that show you all of the COM objects on your system and you can use the same kind of trial-and-error you used with WMI. But this Scripting Guy, at least, has found that hitting up your favorite search engine and looking at both the Web and newsgroup results is probably your best bet. Just try searching on something like, in this case, “scripting windows file extensions.” Try a few different reasonable searches and spend a bit of time digging around the results. What I find happens is that you quickly develop a “lay of the land,” whereby you tap the experience of a bunch of your fellow scripters to identify the options at your disposal. It may be that you did miss a useful WMI class. It may be that this really is a task that involves Active Directory, although you thought it wasn’t. In any case, you’ll end up with a few possible enablers to explore further. Often MSDN and TechNet are good places to go next to dig into more detail about the enablers.

Now what happens if you do all of this and you still come up short? That’s exactly what happened in the case of the File Types tab of the Folder Options item on Control Panel. There were lots of candidate enablers, but in the end I didn’t find one that enabled me to recreate the functionality of the GUI’s list box. Even if you exhaust the standard enablers, however, there is still hope. It may be that you can read a file, make a registry change, or even install a COM object that enables you to call a Win32 API. These alternatives aren’t elegant and they require you to be more cautious because you aren’t protected by a nice abstraction layer put in place to ensure you don’t completely hose your systems (this is particularly important when working directly with the registry), but if you need to accomplish a task, sometimes you have to accept the inelegancies.

If you find yourself writing a script that doesn’t use one of the standard enablers, it really pays to invest some time in ensuring that you have a firm grasp of the feature you’re working with and the effects of any changes you’re going to make. How in the world do you do that? Well, again, it’s a bit of trial and error. If you’re making a registry change, for instance, grab the regmon.exe tool from www.sysinternals.com and monitor the changes to the registry that occur when you use the GUI tool you’re trying to emulate. Dig through MSDN and TechNet looking for official information from Microsoft. (For example, you might find the Registry Reference helpful. It’s not terribly comprehensive, but you should at least check it out.) You can also look for articles from authors you trust that discuss registry settings and their effects.

Scripting the GUI

After all of that preamble, let’s write our script. As you may have gleaned, we won’t be using a standard enabler. Actually, we are using WMI, but just as a way to manipulate the registry. The list of file extensions recognized on a machine are stored as keys under HKEY_CLASSES_ROOT, as you can see in the following regedit.exe screenshot.

HKEY_CLASSES_ROOT

So, to recreate the Extensions column of our list box, we need to use WMI to read these key names. However, file extensions aren’t the only things stored under HKEY_CLASSES_ROOT, so we need a way to distinguish them from other types of keys. Each key that represents a file extension begins with a ‘.’, so we’ll use that to identify them. When we retrieve all subkeys of HKEY_CLASSES_ROOT, they are returned in alphabetical order. Only the ‘*’ key, which houses shell information about all files, appears before the file extension keys. So, we can easily filter for the keys we’re interested in by checking the first character of the key name to see if it’s a ‘.’. If it isn’t, then it must either be the ‘*’ key, in which case we don’t display any information, but continue with the script or we’ve reached the end of the ‘.’ keys, in which case we use WScript.Quit to end the script.

The following script uses the WMI registry provider to connect to the computer identified in the strComputer variable and returns a list of all the registered file extensions on that computer.

Const HKEY_LOCAL_MACHINE = &H80000002
strComputer = "."
 
Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\root\default:StdRegProv")
 
strRootKeyPath = "SOFTWARE\Classes"
objReg.EnumKey HKEY_LOCAL_MACHINE, strRootKeyPath, arrSubKeys
 
For Each objSubKey In arrSubKeys
   If Left(objSubKey,1) = "." Then
      WScript.Echo objSubKey
   ElseIf Not Left(objSubKey,1) = "*" Then
     WScript.Quit
   End If
Next

There are a few items of note in this script. First, we’re making use of the registry provider which resides in the root\default namespace, not the root\cimv2 namespace that most of us are more familiar with. Second, notice how when we call objReg.EnumKey we don’t appear to be getting any results back. We are getting results back, but they get stored in one of the parameters we provide to the EnumKey method, arrSubKeys. This is what is known as an out parameter. Admittedly, it looks a little unnatural, but what can I say, that’s how it works.

So we can get the extensions, but what about the corresponding descriptions? We have to do a little more work to get those. Take a look at the regedit.exe screenshot above. The default entry in the .acf key has the value “Agent.Character.2”. It turns out that there’s another key at the same level in the hierarchy as the .acf key, named “Agent.Character.2”. And its default value is a description of the corresponding file format – exactly what we’re after.

File Description in HKCR

The following script extends the first one by reading the default value in the extension key, binding to the corresponding key and reading its default value – which is a textual description of the file type. Note: You must use cscript.exe to run this script because it uses WScript.StdOut.Write to avoid the carriage return linefeed automatically inserted by WScript.Echo.

Const HKEY_LOCAL_MACHINE = &H80000002
strComputer = "."
Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\root\default:StdRegProv")
 

strRootKeyPath = "SOFTWARE\Classes"
objReg.EnumKey HKEY_LOCAL_MACHINE, strRootKeyPath, arrSubKeys
 
For Each objSubKey In arrSubKeys
   If Left(objSubKey,1) = "." Then
      WScript.StdOut.Write " " & objSubKey 
      strKeyPath = "SOFTWARE\Classes\" & objSubkey
      strValueName = ""
      objReg.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue
      WScript.StdOut.Write  " " & strValue
      If Not IsNull(strValue) Then
         strKeyPath = "SOFTWARE\Classes\" & strValue
         strValueName = ""
         objReg.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strNewValue
         WScript.Echo  " " & strNewValue
      Else
         WScript.Echo
      End If
   ElseIf Not Left(objSubKey,1) = "*" Then
      WScript.Quit
   End If
Next

Running this script results in the output we want – the contents of a registered file types list box.

Script Output

This script may look a little intimidating to some of you. So, let’s have a closer look at the new code. The first additional thing we needed to do was to read the default value from the extension key. Here’s the code that does that:

strKeyPath = "SOFTWARE\Classes\" & objSubkey
strValueName = ""
objReg.GetStringValue HKEY_LOCAL_MACHINE, strKeyPath, strValueName, strValue

We have the extension key name stored in objSubkey, so we leverage that to construct a string, stored in strKeyPath, that represents the full path to the subkey. Since we want the value of the default entry, we set strValueName to the empty string. Last, we call the registry provider’s GetStringValue method. Like the EnumKey method used earlier in the script, GetStringValue uses the concept of an out parameter to return results. In the code snippet above, the result will be stored in the strValue variable.

The value we get back in strValue is going to be a string that, like “Agent.Character.2” in our example, indicates which key under HKLM\Software\Classes houses the file description as its default entry. The following section of code opens that key and retrieves the corresponding file description – which is stored in the default entry. If strValue is found to be null, we still want to move to the next line – which is what WScript.Echo accomplishes.

If Not IsNull(strValue) Then
         strKeyPath = "SOFTWARE\Classes\" & strValue
         strValueName = ""
         objReg.GetStringValue HKEY_LOCAL_MACHINE,_ strKeyPath, strValueName, strNewValue
         WScript.Echo  " " & strNewValue
Else
         WScript.Echo
End If

So, there you have it. You can now get the extensions and file descriptions recognized on any computer rather than just the computer you happen to be working on. You’re well on your way to becoming a Scripting Guy. Join us next time as we move beyond gathering information about extensions and learn how to write scripts that manage them. Oh, and please drop us a line if there’s a particular GUI tool you’d like us to cast a scripting eye upon and we’ll do our best to accommodate.