Hey, Scripting Guy!Be Careful What You Say

The Microsoft Scripting Guys

DURING THE AMERICAN Civil War (at the Battle of Spotsylvania, to be precise), Union General John Sedgwick was dismayed to find his men cowering in the face of the occasional random shot fired in their direction by Confederate snipers. According to legend, Sedgwick told the men: "I'm ashamed of you, dodging that way. They couldn't hit an elephant at this distance." Moments later, Sedgwick became the highest-ranking Union officer killed during the war. Maybe the Confederate snipers couldn't hit an elephant at that distance, but, unfortunately for Sedgwick, they could hit a general.

Of course, it goes without saying that the moral of the story is-well, now that you mention it, "Keep your head down if someone is shooting at you" would be a pretty good moral, wouldn't it? Wow-too bad we didn't think of that. Instead, we came up with the following moral: "Be careful what you say, because someday it might come back to haunt you." It was true for General John Sedgwick, and it's been true for many other people as well. For example, no one remembers Thomas Watson, long-time head of IBM, as one of the great entrepreneurs in American history. Instead, Watson has been immortalized as the man who said, "I think there is a world market for maybe five computers."

Interestingly enough, there's no evidence that Watson ever actually said this; at best he may simply have mentioned someone else's thoughts on the matter. But who cares? After all, the idea that the head of IBM might have said that there was no market for computers makes such a good story that we can't let a silly thing like facts get in the way, can we?

Incidentally, Watson did say "If you want to succeed, double your failure rate." That's advice the Scripting Guys have wholeheartedly embraced.

Well, at least the part about doubling your failure rate.

And then there's this quote: "If you're writing VBScript scripts you don't have access to the .NET Framework." Admittedly, telling people that the Microsoft® .NET Framework is inaccessible to scripters isn't quite as memorable as "They couldn't hit an elephant at this distance" or "I think there is a world market for maybe five computers." However, such a statement is equally far off base. It's true that the .NET Framework was designed with C# and Visual Basic® developers in mind, and it's true that VBScript cannot instantiate native .NET Framework classes. But that doesn't mean the .NET Framework is inaccessible to scripters, at least not all of it.

Yes, you're right: we can't deny that the majority of .NET Framework classes remain off-limits to VBScript. However, there are a reasonably large number of .NET classes that have COM-callable wrappers, which simply means these classes include a COM interface enabling them to be accessed from a scripting language like VBScript. You say you find that hard to believe? Then take a look at the HKEY_CLASSES_ROOT portion of your registry shown in Figure 1; you should see all sorts of classes beginning with System.

Figure 1 System Classes in the Registry

Figure 1** System Classes in the Registry **(Click the image for a larger view)

Most of these are .NET Framework classes, and most of them can be instantiated through VBScript.

Told you!

Of course, as anyone who's ever bought a product advertised on late-night TV can attest, just because you can do something doesn't mean you should do it (there's usually a reason why those items aren't available in stores). Sure, you can create an instance of the System.ContextMarshalException class in a script; we just don't have any idea why you'd want to or what you'd do with it.

But that isn't the case for all .NET Framework classes. For example, one of the great bugaboos in the scripting world is the fact that there's no easy way to sort a list of items. To put a list in alphabetical order requires you to either use a disconnected recordset or create a cryptic sorting function all your own, like the following bubble sort code (those of you with weak stomachs might want to skip on to the next paragraph):

For i = (UBound(arrNames) - 1) to 0 Step -1
    For j= 0 to i
        If UCase(arrNames(j)) > 
          UCase(arrNames(j+1)) Then
            strHolder = arrNames(j+1)
            arrNames(j+1) = arrNames(j)
            arrNames(j) = strHolder
        End If
    Next
Next

Don't feel bad: we wrote the code and we aren't totally sure what it all means, either.

So how can the .NET Framework help us in a situation like that? To answer that question, let's take a look at a very simple and straightforward little script:

Set DataList = CreateObject _
      ("System.Collections.ArrayList")

DataList.Add "B"
DataList.Add "C"
DataList.Add "E"
DataList.Add "D"
DataList.Add "A"

DataList.Sort()

For Each strItem in DataList
    Wscript.Echo strItem
Next

As you can see, what we're doing here is creating an instance of the .NET Framework class System.Collections.ArrayList. We then use the Add method to add five items to the list. That's all well and good, except for one thing: we added these items in somewhat haphazard fashion (B, C, E, D, A), but we need to display the items in alphabetical order (A, B, C, D, E). Time to haul out the bubble sort code, right?

Wrong. (Sorry to be so blunt, but wrong is wrong.) As it turns out, we don't need to haul out bubble sort code; all we need to do is call the arraylist's Sort method:

DataList.Sort()

That's it. With that one single line of code we can sort all the items in our array list.

Prove it? Wow, tough crowd. That's OK; we expected there to be a Doubting Thomas or two in the audience. That's why the script includes a For Each loop that loops through all the items in the list, echoing the value of each one to the screen:

For Each strItem in DataList
    Wscript.Echo strItem
Next

Now here's a question for you to tackle: what do we get back when we run this script?

Good guess:

A
B
C
D
E

Nice, isn't it?

Hmmm, we see that some of you aren't wholly satisfied. You say that's well and good, but what you really wanted to do was echo back those values in descending order; that is, you wanted a list that ran from Z to A rather than a list that ran from A to Z. If you can't get that, well . . .

Hey, no problem. All you had to do was ask:

Set DataList = CreateObject _
      ("System.Collections.ArrayList")

DataList.Add "B"
DataList.Add "C"
DataList.Add "E"
DataList.Add "D"
DataList.Add "A"

DataList.Sort()
DataList.Reverse()

For Each strItem in DataList
    Wscript.Echo strItem
Next

You're right: this does look an awful lot like the script we just showed you, doesn't it? However, there is an important addition. Take a look at the single line of code we added, a line that directly follows the Sort method:

DataList.Reverse()

See that? After sorting the list in ascending order, we called the Reverse method which-surprise, surprise-reverses the order of all the items in the array. What does it give us? It gives us output that looks like this:

E
D
C
B
A

It's hard not to be impressed, isn't it?

But wait! It gets even better. Have you ever tried to remove an item from a VBScript array? No, don't cry; we aren't going to ask you to do that. Instead, we're going to show you how easy it is to remove an item from our .NET Framework arraylist. Don't like the letter D? Get rid of it. Here's a script that creates an array list, sorts it, and then removes the entry for D:

Set DataList = CreateObject _
  ("System.Collections.ArrayList")

DataList.Add "B"
DataList.Add "C"
DataList.Add "E"
DataList.Add "D"
DataList.Add "A"

DataList.Sort()
DataList.Remove("D")

For Each strItem in DataList
    Wscript.Echo strItem
Next

Again, you should take a look at the little line of code immediately following the Sort method:

DataList.Remove("D")

Here we've used the Remove method to remove the item equal to D. And here's what we get back when we run the script:

A
B
C
E

Hasta la vista, D!

And don't overlook the best part: we can remove an item without having to resize or redimension the array. Now that's cool. Of course, we were also able to add items to the array without having to resize or redimension things. Try telling us that doesn't make your scripting life easier.

Believe it or not, we've hardly yet scratched the surface of what you can do with the .NET Framework arraylist (for more information, see the arraylist entry in the .NET Framework SDK).

Is the arraylist useful for scripters? Let's put it this way: having seen the .NET Framework arraylist, we're not sure why you'd ever want to use the built-in VBScript array, at least not for simple lists of items like the ones we've used here.

Admittedly, we Scripting Guys haven't exactly made an exhaustive survey of the .NET Framework to find out which classes might be of use to scripters (and which classes might not be). That's something we're working on. In the meantime, feel free to poke around the full .NET Framework SDK. It seems like every time we browse through the SDK we find something new and interesting that script writers can use.

Like what? Well, how about generating random numbers. Want to generate a random number between 1 and 100 using the VBScript random number generator? No problem; just use code similar to this:

Randomize
Wscript.Echo Int((100 - 1 + 1) * Rnd + 1)

Got that formula memorized? If not, don't worry; turns out it's far easier and far more intuitive to use the .NET Framework System.Random class:

Set objRandom = CreateObject("System.Random")
Wscript.Echo objRandom.Next_2(1,100)

Most likely you can figure this out without an explanation. Regardless, we'll give you a brief one, if only because TechNet Magazine has this idea that columnists should actually write something each month. (Beats us why-that's their policy, not ours.)

As you can see, we create an instance of the System.Random class, and then call the Next_2 method, passing the method two parameters:

  • 1, the first number in our range (remember, we're hoping to generate a random number between 1 and 100).
  • 100, the high number in our range.

That's all we have to do.

Looks like we have time for one final question. Yes, you in the green shirt. Who exactly was it who said script writers couldn't access the .NET Framework? Well, to be perfectly honest, when we first started we-oops, looks like we're out of time after all. See you next month!

Good Times, Good Times

February is almost here, which can only mean one thing. No, not Valentine's Day. No, not President's Day, either. Oh, for goodness sakes, Groundhog's Day? No, the closer we get to February the closer we get to the 2007 Winter Scripting Games, the world's foremost (probably the world's only) international scripting competition. This year's Games promises to be bigger and better than ever; this time around we'll have four divisions:

  • Beginners VBScript
  • Advanced VBScript
  • Beginners Windows PowerShell™
  • Advanced Windows PowerShell

Check out the 2007 Winter Scripting Games home page for more information.

What's that? Oh, thanks. And, uh, a Happy Groundhog's Day to you, too.

Download Windows PowerShell Today

Windows PowerShell 1.0 is now available for download. And it's even free! How can you pass up that deal? Download the software and start using it today.

The Microsoft 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.