Hey, Scripting Guy!Scripting Around the Squiggly Red Line

The Microsoft Scripting Guys

Download the code for this article: HeyScriptingGuy2007_06.exe (151KB)

When you’re a Microsoft Scripting Guy, you get used to people falling all over themselves in order to do things for you:

"Can I get you a fresh cup of coffee, Scripting Guy?"

"Hey, Scripting Guy! There’s only one cookie. Why don’t you take it?"

"Get up, Grandma. Let the Scripting Guy have the chair."

Yes, everyone wants to help the Scripting Guys—everyone. Well, everyone except Microsoft® Word.

That’s right, Microsoft Word. Now, in general, the Scripting Guys like Microsoft Word. And we suppose that it’s possible that Word has better things to do than cater to the Scripting Guys’ every whim. (Although we can’t imagine what could be better than that.) Still, it’s hard to deny that Microsoft Word can be a bit lazy at times. For example, take a look at Figure 1, a screenshot showing an extract from a draft of this column.

Figure 1 Document in Word showing spelling mistakes

Figure 1** Document in Word showing spelling mistakes **(Click the image for a larger view)

Notice anything peculiar? That’s right: Word has drawn squiggly lines under scripting terms like Msgbox. Why? Because Word doesn’t recognize those terms. Doesn’t recognize those terms? Come on, Word; after all, over the past few years the Scripting Guys have typed the term Msgbox several million times. Several million more times they have run spell-check and told you that Msgbox was spelled correctly. And yet still you mark Msgbox and StrReverse and LTrim as being misspelled words? What’s up with that?

Now, before you rush to the defense of Word, yes, we know that we can manually add these terms to the Microsoft Word dictionary. But, come on, we’re the Scripting Guys, for crying out loud; what do we know about manual labor? Besides, manually adding VBScript functions and Windows® Management Instrumentation (WMI) class names and Active Directory® Security Interfaces (ADSI) object attributes can be a bit tedious, to say the least. And what if we want to do this on all of our computers? What if we want to share these correct spellings with our devoted readers? What if... well, you get the idea.

The point is this: surely there must be an easier way to add scripting terms (or any terms, for that matter) to the dictionary without having to do it by hand, one word at a time.

As it turns out, there isn’t a way. See you all next month.

No, hey, we’re just kidding. Of course there’s an easier way to go about adding terms to the dictionary. And in just a moment we’ll show you how to do that. First, however, let’s take a minute to chat about dictionaries.

Speaking of which, here’s an interesting fact about dictionaries. Why do Americans spell color without a u (color, not colour)? Why do Americans spell the word as center rather than centre? We know the answer: because Noah Webster, who put together the first American dictionary, didn’t like English spelling rules and thus somewhat arbitrarily changed the spelling of many words.

Well, OK. Not enthralling. But you try coming up with an interesting fact about dictionaries!

In addition to the standard dictionary built into the application, Microsoft Word also allows you to create any number of custom dictionaries. By default, Word comes with a custom dictionary named Custom.dic, which is typically found in the Application Data\Microsoft\Proof folder within your User Profile folder (\Documents and Settings\username by default in Windows XP). If you right-click a misspelled word and select Add to Dictionary, that word will be added to Custom.dic.

As it turns out, Custom.dic is just a plain old text file. Open it up in Notepad and you’ll see a list of words similar to this:

Aaberg
Aalling
Abbas
Abercrombie
Abola
Abolrous
Abrus

And that’s how we can programmatically add scripting terms to the dictionary in Microsoft Word. All we have to do is create a text file containing those terms, then configure Word to use that file as a custom dictionary. How hard is that going to be? Considering the fact that the Scripting Guys are willing to do this ourselves, obviously not very hard at all.

But hold on: we need to take one more quick side trip before doing that. In the Microsoft Word object model, information about custom dictionaries is stored in the CustomDictionaries collection. In turn, that collection can be programmatically accessed by referencing the Word.Application object and the CustomDictionaries property. For example, Figure 2 shows a script that returns information about all the custom dictionaries associated with your copy of Microsoft Word.

Figure 2 Script that retrieves custom dictionary information

Set objWord = CreateObject(“Word.Application”)

Set colDictionaries = objWord.CustomDictionaries

For Each objDictionary in colDictionaries
  Wscript.Echo “Name: “ & objDictionary.Name
  Wscript.Echo “Language ID: “ & objDictionary.LanguageID
  Wscript.Echo “Language-specific: “ & objDictionary.LanguageSpecific
  Wscript.Echo “Location: “ & objDictionary.Path
  Wscript.Echo “Read-only: “ & objDictionary.ReadOnly
  intType = objDictionary.Type
  Select Case intType
    Case 0 Wscript.Echo “Type: Spelling”
    Case 1 Wscript.Echo “Type: Grammar”
    Case 2 Wscript.Echo “Type: Thesaurus”
    Case 3 Wscript.Echo “Type: Hyphenation”
    Case 4 Wscript.Echo “Type: Spelling (complete)”
    Case 5 Wscript.Echo “Type: Spelling (custom)”
    Case 6 Wscript.Echo “Type: Spelling (legal)”
    Case 7 Wscript.Echo “Type: Spelling (medical)”
    Case 8 Wscript.Echo “Type: Hangual-Hanja Conversion”
    Case 9 Wscript.Echo “Type: Hangual-Hanja Conversion (custom)”
  End Select
  Wscript.Echo
Next

objWord.Quit

Run the script in Figure 2, and you’ll get back information similar to this:

Name: CUSTOM.DIC
Language ID: 0
Language-specific: False
Location: C:\Documents and Settings\kenmyer\Application Data\Microsoft\Proof
Read-only: False
Type: Spelling (custom)

Pretty slick, huh?

Of course, getting information about existing dictionaries isn’t quite the same thing as creating a new custom dictionary; it’s sort of like the difference between reading about someone who has a million dollars and being someone who has a million dollars. Therefore, let’s see if we can figure out how to create a new custom dictionary in Word.

We’re going to start out by adding all the VBScript functions to a new dictionary named Scripting.dic. We’re starting out with VBScript functions because we have to do a little work here. After that, however, we’ll show you how to add WMI classes, ADSI attributes, and Windows PowerShell™ cmdlets to your dictionary without having to do hardly any work at all. (Needless to say, when it comes to hardly doing any work at all, the Scripting Guys know what they’re talking about.)

To begin with, creating a custom dictionary in Microsoft Word is as easy as this:

Set objWord = CreateObject _
  (“Word.Application”)

Set colDictionaries = _
  objWord.CustomDictionaries
colDictionaries.Add “Scripting.dic”

objWord.Quit

As you can see, we start out by creating an instance of the Word.Application object. You might notice that we never set the Visible property to True, and thus never make Word visible onscreen. Why not? Well, simply because this script requires only a second or so to run. Because of that, we didn’t see much reason to have Word flash briefly onto the screen and then immediately disappear. But if you like flashing instances of Word (if for no other reason than to let you know that the script is actually doing something) then simply add this as the second line of code in your script:

objWord.Visible = True

After creating the Word.Application object, we next use this line of code to create an object reference to the CustomDictionaries collection:

Set colDictionaries = _
  objWord.CustomDictionaries

We then call the Add method to add the custom dictionary. Note that this file doesn’t have to exist; if Word can’t find a file named Scripting.dic, then it will simply create a new, blank file by that name. Here’s the code for adding the custom dictionary:

colDictionaries.Add “Scripting.dic”

You might have noticed as well that we didn’t specify a path when adding the new dictionary. Instead, we simply handed the file name (Scripting.dic) to the Add method. That’s fine. In that case, Scripting.dic will be created in the default folder for dictionaries and other proofing tools (for example, C:\Documents and Settings\kenmyer\Application Data\Microsoft\Proof).

What if we wanted our dictionary to reside in a different location? No problem. This command creates Scripting.dic in the C:\Scripts folder:

colDictionaries.Add _
  “C:\Scripts\Scripting.dic”

And what if we decide later on that we want to get rid of that custom dictionary? Once again, that’s no problem. In that case, we can just bind to the dictionary in question and use the Delete method to get rid of it:

Set objWord = CreateObject _
  (“Word.Application”)
Set objDictionary = _
  objWord.CustomDictionaries _
  (“C:\Scripts\Scripting.dic”)
objDictionary.Delete 

objWord.Quit

Note that this removes C:\Scripts\ Scripting.dic from the CustomDictionaries collection in Word. However, it does not delete the file itself. If you motor on over to C:\Scripts, the Scripting.dic file will still be there, just the way you left it.

At this point, all we have to do is start adding terms to our custom dictionary. One way to do that is to simply find a list of, say, VBScript functions, copy them, and then paste them into the file Scripting.dic. For example, you can find a list of VBScript functions in the VBScript Language Reference on MSDN®. Copy those terms, paste them into Scripting.dic, then fire up Word. Figure 3 shows the same section of this article we showed you earlier in Figure 1. See that squiggly red line beneath Msgbox? Of course you don’t; that’s because Word now views Msgbox as a correctly spelled word.

Figure 3 Document in Word after adding a custom dictionary

Figure 3** Document in Word after adding a custom dictionary **(Click the image for a larger view)

That’s pretty handy, if we do say so ourselves, especially when you consider the fact that you can share Scripting.dic and your custom dictionary creation script with anyone you want. Do so and in no time at all their versions of Word will also view Msgbox as a correctly spelled word. The truth is, if we Scripting Guys were lazy, we could just end this month’s column right here and now and you’d still walk away with something useful. But, as the saying goes, you ain’t seen nothin’ yet.

Note: actually, we are lazy. But seeing as how we had to fill up the excess space in this column, we decided to toss in a bonus script or two.

As you probably know, both VBScript and Windows Script Host are kind of shy; they don’t really like to talk about themselves. That isn’t true of WMI, ADSI, or Windows PowerShell, however. All you have to do is ask and those technologies will tell you all about their classes, properties, methods—pretty much anything and everything you’d ever want to know.

Take ADSI, for example. Figure 4 shows a script that binds to the Active Directory schema and retrieves all the attributes of the user account object. In turn, it then writes those attributes to the file C:\Scripts\Scripting.dic.

Figure 4 Add ADSI attribute names to a custom dictionary

Const ForWriting = 2

Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objFile = objFSO.OpenTextFile(“C:\Scripts\Scripting.dic”, ForWriting)

Set objSchemaUser = GetObject(“LDAP://schema/user”)

For Each strAttribute in objSchemaUser.MandatoryProperties
  objFile.WriteLine strAttribute
Next

For Each strAttribute in objSchemaUser.OptionalProperties
  objFile.WriteLine strAttribute
Next

objFile.Close

See how cool that is? With just a few lines of code we’ve created a dictionary containing all the attributes of the Active Directory user object. Share this code with your fellow scripters, and none of you will ever have to worry about attribute names like streetAddress or givenName being flagged as misspelled words. And, of course, this is infinitely extensible. Want to add the properties for the computer class? Then substitute in this line of code:

Set objSchemaComputer = _
  GetObject(“LDAP://schema/computer”)

Or how about WMI classes? Figure 5 shows a script that retrieves a list of all the WMI classes found in the root\cimV2 namespace and writes that information to C:\Scripts\Scripting.dic.

Figure 5 Add WMI class names to a custom dictionary

Const ForWriting = 2

Set objFSO = CreateObject(“Scripting.FileSystemObject”)
Set objFile = objFSO.OpenTextFile(“C:\Scripts\Scripting.dic”, ForWriting)

strComputer = “.”

Set objWMIService=GetObject(“winmgmts:\\” & strComputer & “\root\cimv2”)
 
For Each objClass in objWMIService.SubclassesOf()
  objFile.WriteLine objClass.Path_.Class
Next

objFile.Close

Forget our Windows PowerShell users? The Scripting Guys would never do that. Well, OK, actually we did forget you. But we did remember to come up with this one-line script, which returns all the cmdlet names and then writes them to C:\Scripts\Scripting.dic:

Get-Command | Foreach-Object `
{Add-Content c:\scripts\scripting.dic -value `
$_.Name}

The nice thing about this month’s column is that we all learned something. You learned how to create custom dictionaries in Microsoft Word and how to programmatically configure Word to use those custom dictionaries. And the Scripting Guys learned that if you want something done, well, it’s still way better to try and get someone else to do it for you. But if all else fails, you can always do it yourself.

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.