Hey, Scripting Guy!The Adrenaline Rush

The Microsoft Scripting Guys

Code download available at: HeyScriptingGuy2008_07.exe(151 KB)

There's no doubt in our minds that writing for TechNet Magazine is the most prestigious—and rewarding—job anyone could have. (Of course, it would be even more rewarding if we actually got paid, but that's another story.) Just how prestigious is this job? Let's put it this way: right now, all over the world, children who are being tucked into bed are looking up at their mothers and saying, "Mommy, when I grow up, I want to write a monthly scripting column for TechNet Magazine."

Note Of course, there are people all over the world who believe that this column is already written by children. But that, too, is another story.

It's well-known that the Scripting Guys enjoy basking in the adulation that comes with writing for TechNet Magazine; we've even learned to put up with the autograph seekers and the paparazzi. But the truth is, we don't do this job for the honor and the glory; we do this job for the pure adrenaline rush that comes with writing system administration scripts.

Admittedly, it's not often you see the terms pure adrenaline rush and system administration scripting in the same sentence. (Unless, of course, pure adrenaline rush is preceded by the complete and total lack thereof.)

The average person considers system administration scripting to be extremely useful but probably not very exciting; in fact, he or she likely finds scripting a bit humdrum. And here's the reason: each of these people haven't tried writing boot configuration data scripts in either Windows Vista® or Windows Server® 2008.

Ah, yes, that raised your heart rate a beat or two, didn't it? As most of you reading this probably know, in Windows Vista and Windows Server 2008, the old boot.ini file has been discarded in favor of a new boot configuration data store that provides increased flexibility (and capabilities) when it comes to managing the boot process.

That's pretty cool. But what's really cool is this: thanks to a new Windows® Management Instrumentation (WMI) provider, the boot configuration data (BCD) store now can be easily accessed—and completely managed—using scripts.

Don't worry about that strange sensation that just ran up and down your spine; that's what a pure adrenaline rush feels like. After you start working with BCD scripts, you'll begin to get used to it.

Before we go much further, we should point out that we can't possibly even begin to cover everything that can be done using the BCD provider, at least not in this one column. (We sent e-mail to the magazine's editors asking about the possibility of doing a special double issue devoted entirely to writing boot configuration data scripts, but we haven't heard back from them yet.)

If you want more complete information, check out the BCD WMI provider documentation at go.microsoft.com/fwlink/?LinkId=116953. In the meantime, we'll show you some sample code designed for multi-boot computers—computers with more than one operating system installed.

Oh, these scripts work only on Windows Vista and Windows Server 2008. That's because, as we mentioned earlier, these are the only two operating systems that currently support BCD.

Let's start by taking a look at a script that tells us which OS is currently in use on a computer. Granted, that's not the most exciting use for the BCD provider; after all, we can already use the WMI class Win32_OperatingSystem to determine which OS is currently in use on a computer. However, this is a reasonably simple script (as far as BCD scripts go, at least), and it provides a good way to illustrate the basic techniques required when working with BCD. (Techniques that are, to say the least, a tad bit … unusual.)

Besides, we can't include too much excitement in our columns: TechNet Magazine has already told us that we will be liable for any damages caused by overexcited and overzealous fans of Hey, Scripting Guy! Considering the general mayhem that erupted around the world after our column on regular expressions, well, we can't afford to do anything that exciting ever again.

Note Remember all those videos of the Beatles being mobbed by their fans, and all those pictures of girls fainting and crying as John, Paul, George, and Ringo came into view? That's pretty much what things are like for us each time we publish a new column.

Well, OK, that might be something of an exaggeration. But there's no doubt that a lot of people—men as well as women—sob after reading one of our columns.

At any rate, take a look at the script in Figure 1, which uses the BCD provider to determine which OS is currently in use on a computer. Before you try to run this script, you should know that you must be running as an administrator or else the script will fail.

Figure 1 Getting the current operating system

Const BcdLibraryString_Description = &h12000004
Const Current = "{fa926493-6f1c-4193-a414-58f0b2456d1e}"

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore
objStore.OpenObject Current, objDefault

objDefault.GetElement BcdLibraryString_Description, objElement
Wscript.Echo "Current operating system: " & objElement.String

And no, that doesn't mean you simply need to be logged on to an account with administrator privileges. It means that you need to open a command prompt window by right-clicking Command Prompt in the Start menu and selecting Run as administrator. Then you can run this script from the command prompt.

So how does this script work? You know, we were afraid you were going to ask. But that's all right; we'll try to explain the best we can (which, if nothing else, should keep the excitement to a minimum, at least for a while).

We start out by defining two constants: BcdLibraryString_Description and Current. BcdLibraryString_Description refers to the object we want to retrieve; in this case, that object contains the Description (name) of the current operating system.

This, by the way, is one of the endearing quirks of working with the BCD provider. Typically, you don't simply retrieve the value of a property; instead, you use one of these constant values (and the GetElement method) to retrieve another object, then echo back the value of a property belonging to that object. Sure, that's a little goofy, but you know what they say: getting there is half the fun.

Note Which kind of makes you wonder where they were going, doesn't it?

As for the constant Current, well, that's simply a GUID that represents the operating system currently in use. The value

 {fa926493-6f1c-4193-a414-58f0b-2456d1e} 

is considered a "well-known" GUID (which, if nothing else, proves that people at Microsoft actually do have a sense of humor).

Despite the fact that this is a well-known GUID, we went ahead and included the value anyway, just in case some of you reading this have trouble remembering which of the following two values is correct:

{fa926493-6f1c-4193-a414-58f0b2456d1e} 
{fa926493-6f1c-4193-a414-58f0b2456d1f}

Note In case you're wondering, GUID can be pronounced either like "gwid" or like "goo-id." We could tell you more, like the fact that the preceding GUID must be a V1 GUID, seeing as how the third group of digits begins with a 4. But, we did promise to try and keep the excitement to a minimum. And we keep our promises.

As you might have guessed, you'll need to use constants, hexadecimal values, and GUIDs pretty much everywhere you turn when working with the BCD provider. We can't cover all of these items in this month's column, but you can visit the BCD page we mentioned earlier on MSDN® (go.microsoft.com/fwlink/?LinkId=116953) for more information. If you can stand all the excitement, that is.

After defining our two constants, we next connect to the WMI service on the local computer. Can you use this script to retrieve boot configuration data for a remote machine? Of course you can.

To be honest, if you couldn't, this BCD stuff would be of only minimal value. In order to retrieve boot configuration data from a remote machine (again, the remote computer must be running either Windows Vista or Windows Server 2008), you simply assign the name of that machine to the variable strComputer:

strComputer = "atl-fs-001"

We should also point out a couple of key items in our WMI connection string. To begin with, you might have noticed that we included both the Backup and Restore privileges in our connection string; that's what the {(Backup,Restore)} construction does. Is that important? Well, if you want your script to work, then it's very important: so if you don't explicitly include those two privileges, the script will fail.

Second, note that we don't connect to the root\cimv2 namespace, the namespace most commonly used in system administration scripts. Instead, we connect to the root\WMI namespace and bind directly to the BCDStore class. That's what this bit of code is for:

"\root\wmi:BcdStore" 

And no, that wasn't the exciting part. The exciting part is yet to come.

Although there might be an exception or two somewhere, for the most part any BCD script you write is going to kick off with the same three steps: define the constants, connect to the WMI service, and then open the BCD store. We've already completed Steps 1 and 2; the following line of code takes care of Step 3:

objStoreClass.OpenStore "", objStore

Like we said, here we're opening the BCD store, the operating system entity that stores all your boot configuration information. To open the store we simply call the OpenStore method, passing the method two parameters:

  • An empty string (""). This tells the script that we want to open the default store.
  • objStore. This in an "out" parameter that we supply to the script. We give the method the name of a variable and, in a gesture of gratitude, the method returns an object (in this case, an object representing the BCD store) that uses that variable name as an object reference.

As soon as the store is open, we can use the OpenObject method to retrieve yet another object (stored in the out parameter objDefault):

objStore.OpenObject Current, objDefault

And what is this new object? That's right: it's the operating system currently in use. This is something we know because we passed OpenObject the constant Current, the well-known GUID that represents the current operating system.

OK, so now we know which operating system is currently in use on the computer, right? Well, almost. To get that information, we still need to use GetElement to retrieve an object representing the Description of that operating system:

objDefault.GetElement _
  BcdLibraryString_Description, objElement

Now, do you promise you won't get overly excited and engage in any sort of general mayhem? Good. In that case, we can tell you that now we can echo back the value of the String property and—finally!—determine the operating system currently in use on the computer:

Wscript.Echo "Current operating system: " _
  & objElement.String

Please remain calm. Remember your promise: no general mayhem. Yes, we know it's hard. Just do the best you can. Try taking deep breaths; that always works for us when we're writing BCD scripts.

Like we said, that's a lot of trouble to go through just to determine which operating system is in use on a computer; there are easier ways to accomplish that task. On the bright side, however, you now understand how a BCD script works, which means you can do some things that script writers have never been able to do. (Stay calm, stay calm.) For example, on a multi-boot computer, one operating system is always tagged as the "default"; if a computer reboots and no one is around to tell it different, the computer will automatically load the default operating system. Prior to Windows Vista (and the BCD provider), there was no way for a scripter to determine the default operating system on a computer. Now, however, that's as easy as running a script like the one in Figure 2.

Figure 2 Determining the default operating system

Const BcdLibraryString_Description = &h12000004
Const BootMgrId = "{9dea862c-5cdd-4e70-acc1-f32b344d4795}"
Const DefaultType = &h23000003

strComputer = "." 

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
    strComputer & "\root\wmi:BcdStore")
objStoreClass.OpenStore "", objStore

objStore.OpenObject BootMgrId, objBootMgr 

objBootMgr.GetElement DefaultType, objDefaultOSIdentifier
objStore.OpenObject objDefaultOSIdentifier.Id, objDefault

objDefault.GetElement BcdLibraryString_Description, objElement 
WScript.Echo "Default operating system: " & objElement.String

You're right: this does call for a little general mayhem, doesn't it? But just a little, OK? We aren't going to explain this script in any great detail; assuming you didn't just skip over the preceding 10 or 12 paragraphs, you should be able to follow the logic for yourself. You might note, however, that in order to get at the default operating system, we have to take an intermediate step: we have to use the OpenObject method to open an instance of the Boot Manager object. After we open the Boot Manager, we can use the constant DefaultType to retrieve an object representing the default operating system.

Now, admittedly, that's pretty exciting: we can determine the current operating system, and we can determine the default operating system. But you know what would be really cool? That would be to retrieve a list of all the operating systems installed on a computer. Now that would be exciting! Fasten your seat belts and look at Figure 3.

What are we doing here? Well, to begin with, we're defining two new constants: WindowsImages, which lets us retrieve instances of all OSs that support BCD (that is, Windows Vista and Windows Server 2008); and LegacyImages, which lets us retrieve instances of all the "legacy" operating systems on the computer. After we connect to the BCD store, we then use the EnumerateObjects method to retrieve all the instances of the BCD-capable operating systems installed on the computer:

objStore.EnumerateObjects _
  WindowsImages, colObjects 

Figure 3 Finding all operating systems on a computer

Const BcdLibraryString_Description = &h12000004
Const WindowsImages = &h10200003
Const LegacyImages = &h10300006

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore 

objStore.EnumerateObjects WindowsImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next
Wscript.Echo

objStore.EnumerateObjects LegacyImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next

After EnumerateObjects does its thing, we will then set up a For Each loop to loop through all the operating systems in that collection. Inside this loop, we use these two lines of code to retrieve and then display the operating system Description:

objObject.GetElement _
  BcdLibraryString_Description, objElement 
Wscript.Echo objElement.String

And then we repeat the process with any legacy operating systems installed on the machine:

objStore.EnumerateObjects _
  LegacyImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement _
  BcdLibraryString_Description, objElement 
 Wscript.Echo objElement.String
Next

Note We understand the thrill involved, but please run your BCD scripts responsibly; there is probably a limit to the number of adrenaline rushes the human body can withstand. Women who are pregnant, or may become pregnant, or may never become pregnant, or who were once pregnant, or who aren't even women after all but are actually men should not run BCD scripts without first checking with their doctor.

Well, OK, you don't need to check with your doctor first. But it would be interesting to hear what a doctor would say if you did ask him about running BCD scripts, wouldn't it?

Now let's try something really crazy; let's see if we can change the default operating system. For example, suppose we have a dual-boot computer running both Windows Vista and Windows Server 2008. And suppose we want to set Windows Vista to be the default operating system. How are we going to do that? Well, take a look at Figure 4. It shows one way to accomplish this task.

Figure 4 Changing the default operating system

Const BootMgrId = "{9dea862c-5cdd-4e70-accl-f32b344d4795}"
Const BcdLibraryString_Description = &h12000004
Const DefaultType = &h23000003
Const WindowsImages = &h10200003

strComputer = "."

Set objStoreClass = GetObject("winmgmts:{(Backup,Restore)}\\" & _
 strComputer & "\root\wmi:BcdStore")

objStoreClass.OpenStore "", objStore 
objStore.EnumerateObjects WindowsImages, colObjects 

For Each objObject in colObjects
 objObject.GetElement BcdLibraryString_Description, objElement 
 If Instr(objElement.String, "Vista") Then
  objStore.OpenObject BootMgrId, objBootMgr 
  objBootMgr.SetObjectElement DefaultType, objObject.ID 
 End If
Next

With this script, we're once again opening the BCD store, then we'll be using EnumerateObjects to retrieve a collection of all the BCD-aware operating systems installed on the computer. From there, we set up a For Each loop to loop through all the items in the collection, using the following (and by now familiar) line of code in order to retrieve the Description for each operating system:

objObject.GetElement _
  BcdLibraryString_Description, objElement

As soon as we have the Description for a given operating system, we use the InStr function to see if the word Vista appears anywhere in that value:

If Instr(objElement.String, "Vista") Then

And you're right, that does seem a little clunky, doesn't it? The cool way would be to use the GUID for Windows Vista and then directly open that operating system, without having to enumerate and walk through all the operating systems on the computer.

Unfortunately, the cool way would require us to know the GUID for Windows Vista. Instead, this way we don't have to know anything (always a bonus for the Scripting Guys); all we have to do is keep searching until we find an operating system with the word Vista in the title.

Note What if you have multiple instances of Windows Vista installed on the computer? Well, in that case you might have to look for a string like Vista Ultimate or Vista Enterprise.

As soon as we find Windows Vista, we use these two lines of code to open the Windows Boot Manager and then set the default operating system to, well, Windows Vista:

objStore.OpenObject BootMgrId, objBootMgr 
objBootMgr.SetObjectElement _
  DefaultType, objObject.ID 

That's all we have time for this month; after all, even the Scripting Guys can't handle non-stop excitement 24 hours a day, 7 days a week. But don't worry; we'll be back next month with another exciting installment of Hey, Scripting Guy! With any luck, you'll have recovered by then.

Dr. Scripto's Scripting Perplexer

The monthly challenge that tests not only your puzzle-solving ability but also your scripting skills.

July 2008: VBScript Squares

OK, so they're probably rectangles, not squares, but that's beside the point. To solve this puzzle, fit each square on the right into an empty square on the left to create the names of VBScript functions. Each square will be used once. Here's an example:

To correctly solve this example, you need to move the OU square (rectangle) to the blank square in the first word, and the MS and the OX to the blank squares in the second word. This results in the VBScript functions UBound and MsgBox, as shown here:

Now you try it:

ANSWER:

Dr. Scripto's Scripting Perplexer

Answer: VBScript Squares, July 2008

The Scripting Guys work for—well, are employed by—Microsoft. When not playing/coaching/watching baseball (and various other activities), they run the TechNet Script Center. Check it out at www.scriptingguys.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.