The Scripting Runtime Objects

Overview

This chapter will introduce some powerful objects that are available for use in your VBScript code. You can think of these as utility objects, as they are designed to be reusable in a variety of situations. We will introduce in this chapter the Dictionary object, which is a useful and more versatile replacement for arrays, as well as the family of objects in the FileSystemObject hierarchy. The objects in the FileSystemObject object collection offer pretty much everything you need for interacting with the Windows file system.

This chapter will start off with a brief overview of the basic syntax, rules, and recommendations for using objects in VBScript. For those who have been working through the previous chapters on VBScript fundamentals, the first sections of this chapter will continue along that course. The rest of the chapter will introduce some powerful objects you will find useful in many of your scripts. If you are already familiar with the basics of using objects in VBScript, and if you are looking primarily for how-to information on the runtime objects, you may wish to skip ahead to those sections of the chapter.

What Are Runtime Objects?

Why do we refer to these as 'runtime objects?' These objects are labeled runtime because they exist in a separate component, apart from the core VBScript interpreter. They are not an official part of the VBScript language. In fact, since they are in a separate component (commonly referred to as the 'scripting runtime'), they can also be invoked from Visual Basic or any other COM-enabled language.

That said, these runtime objects are going to be automatically available to you pretty much anywhere you would use VBScript, be it within Office, Active Server Pages, or the Windows Script Host. So while it's interesting to know that these runtime objects are not officially part of VBScript, it is not essential knowledge. However, the distinction is helpful given that the official Microsoft scripting documentation has the runtime objects information in a separate section labeled 'Script Runtime'.

The Built In Objects Debug, Err, and RegExp

Going back to that 'runtime objects' distinction, there are two other similar objects that we won't be considering in this chapter. We refer to these objects as 'built-in object' because, unlike the runtime objects, these are an inherent part of VBScript. None of the built-in objects will be covered in this chapter. For information on the Debug and Err objects, please refer to the error handling chapter, Chapter 6. For information on the RegExp object and its cousin the Match object, please refer to Chapter 9, which covers regular expressions in detail.

Creating Objects

Throughout the example scripts in this chapter, we will be using the scripting runtime objects to accomplish various tasks . In this section we will quickly go over the syntax for creating (also known as instantiating ) objects. Objects are a bit different than other data types because objects must be created explicitly. With a normal numeric variable, for instance, you just use the equals ( = ) operator to put a number into it. With objects, however, you must first use a separate command to create the object, after which you can use the object in your code.

When an object is instantiated , a copy of that type of object is created in memory, with a pointer (also known as a reference ) to the object stored in the variable you have declared for it. This copy of the object is dedicated to the code that created it. To create an object, you must declare a variable for it, and then use the Set command in conjunction with the CreateObject function. Here is an example (CREATE_OBJECT.VBS).

Option Explicit Dim objDict Set objDict = CreateObject("Scripting.Dictionary") MsgBox "The Dictionary object holds this many items: " & _ objDict.Count

This code declares a variable to hold the object ( objDict ), and then uses the Set command and the CreateObject function to instantiate the object initializing the value of an object variable. This is the only use for the . The MsgBox line then displays the value of the object's Count property. The Set command must be used whenever Set command.

The CreateObject command actually does the real work of creating the object. Whenever you use CreateObject , you must pass into it the name of the object you wish to instantiate, in this case Scripting.Dictionary . Scripting is the name of the library in which the object's class lives (in this case the scripting runtime library), and Dictionary is the class name of the object. Whenever you wish to instantiate an object of a particular type, you just have to know the library and class name of the object so that you can pass it to CreateObject . The documentation for any object you wish to use should tell you this information.

Not all objects can be instantiated directly in this way. Many libraries, including the scripting runtime, have certain objects that can only be created directly by another object in the library. With these types of objects, you cannot use the CreateObject function to instantiate them. Instead, one of the other objects in the library must create them for you. We'll be getting to the details of the FileSystemObject later in this chapter, but we'll jump ahead just briefly to give an example of an object type that cannot be directly instantiated.

The Folder object in the FileSystemObject is not directly creatable. Only the CreateFolder or GetFolder methods can give you a Folder object. The following code illustrates how this works using the GetFolder method ( NOT_DIRECTLY_CREATABLE.VBS ).

Option Explicit Dim objFSO Dim objFolder Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFolder = objFSO.GetFolder("C:") If objFolder.IsRootFolder Then MsgBox "We have opened the root folder." End If

Properties and Methods

Objects have two different elements that you can access with your code: properties and methods . A property is, like a variable, a placeholder for a value associated with that object. A property may hold any type of data, from strings to numbers to dates. A property may also hold a reference to another object. Some properties are read-only, while others can be set with your code. To find out which ones are read-only and which ones can be changed, you need to familiarize yourself with the object through its documentation.

We saw two examples of properties in the previous section: the Dictionary object's Count property and the Folder object's IsRootFolder property. We'll see more later in this chapter.

A method is simply a procedure or function that is attached to an object. You can call methods just as you would any other procedure or function (see Chapter 4). We saw one example of a method in the last code example from the previous section: the FileSystemObject 's GetFolder method, which is a function that returns a Folder object.

Just as in the previous examples, when calling a property or method of an object you must use the name of the variable that holds the object, followed by a period, and then by the name of the property or method.

The With Keyword

The With keyword is a handy shortcut that can save you some typing. When you are referring to the same object more than once in a block of code, you can surround the block of code with the With End With construct. Here is an example ( WITH_BLOCK.VBS ).

Option Explicit Dim objFSO Dim objFolder Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFolder = objFSO.GetFolder("C: Program Files") With objFolder MsgBox "Here are some properties from the " & _ "Folder object:" & _ vbNewLine & vbNewLine & _ "Path: " & .Path & vbNewLine & _ "DateCreated: " & .DateCreated & vbNewLine & _ "DateLastAccessed: " & .DateLastAccessed & _ vbNewLine & _ "DateLastModified: " & .DateLastModified End With

Notice how we surround the last block of code with With and End With . In the With statement, we refer to the objFolder object variable, which allows us within that block of code to refer to the Path , DateCreated , DateLastAccessed , and DateLastModified properties without having to refer to the objFolder variable by name each time. The With statement is a convenience that can save you some typing and make your code look a bit cleaner.

Objects Can Have Multiple References

Behind the scenes, an object variable does not really contain the copy of the object. The object itself is held elsewhere in the computer's memory, and the variable only holds a reference to the object. The reason this technical distinction is important is that you need to be aware of when you are dealing with two different objects versus when you are dealing with two references to the same object. Take a look at this example ( REF_TWO_OBJECTS.VBS ).

Option Explicit Dim objDict1 Dim objDict2 Set objDict1 = CreateObject("Scripting.Dictionary") Set objDict2 = CreateObject("Scripting.Dictionary") objDict1.Add "Hello", "Hello" MsgBox "The first Dictionary object holds this many " & _ "items: " & objDict1.Count & vbNewLine & _ "The second Dictionary object holds this many " & _ "items: " & objDict2.Count

This code produces the dialog box shown in Figure 7-1.

Figure 7-1

We have two variables , objDict1 and objDict2 , and we have used Set and CreateObject to instantiate two separate Dictionary objects. Then we used the Dictionary object's Add method to add a string to objDict1 (we'll be getting to the details of the Dictionary object later in this chapter). Notice, however, that we did not add any items to objDict2 . This is reflected in the dialog box, where we see that objDict1 has a Count of 1 whereas objDict2 has a Count of , since we did not add anything to objDict2 . Now, however, take a look at this code ( REF_ONE_OBJECT.VBS ).

Option Explicit Dim objDict1 Dim objDict2 Set objDict1 = CreateObject("Scripting.Dictionary") Set objDict2 = objDict1 objDict1.Add "Hello", "Hello" MsgBox "The first Dictionary object holds this many " & _ "items: " & objDict1.Count & vbNewLine & _ "The second Dictionary object holds this many " & _ "items: " & objDict2.Count

This code produces the dialog box shown in Figure 7-2.

Figure 7-2

Notice that we only used CreateObject one time, with objDict1 . The next line is the key line of code in this example.

Set objDict2 = objDict1

This line sets objDict2 equal to objDict1 , which means that two variables are holding references to the same object . That is why the Count properties of both objDict1 and objDict2 have a value of 1 , even though we only called the Add method on objDict1 . Since both variables hold references to the same object, it does not matter which variable you use to make a change to that object-both variables will reflect the change.

Object Lifetime and Destroying Objects

When it comes to the issue of variable lifetime (see Chapter 4), object variables are a little different than other kinds of variables. The key to understanding this difference lies in the two facts we introduced in the previous section of this chapter: first, an object variable only holds a reference to the object, not the object itself; and second, multiple variables can each hold a reference to the same object.

An object will stay active in memory as long as one or more variables are holding a reference to it. As soon as the reference count goes down to zero, the object will destroy itself automatically. An object can lose its reference to an object in one of two ways: first, if a variable goes out of scope (see Chapter 4), and second, if a line of code explicitly empties out the object by setting it to the special value of Nothing . Let's look at an example ( REF_NOTHING.VBS ).

Option Explicit Dim objDict1 Dim objDict2 Set objDict1 = CreateObject("Scripting.Dictionary") 'The object now exists and has a reference count of 1 Set objDict2 = objDict1 'The object now has a reference count of 2 objDict1.Add "Hello", "Hello" MsgBox "The first Dictionary object holds this many " & _ " items: " & objDict1.Count & vbNewLine & _ "The second Dictionary object holds this many " & _ "items: " & objDict2.Count Set objDict1 = Nothing 'The object still exists because objDict2 still 'holds a reference Set objDict2 = Nothing 'The object's reference count has now gone down to 0, 'so it has been destroyed.

As you read this code, follow along with the comments to see the reference count and lifetime of the object. Notice how we use this syntax to eliminate a variable's reference to the object.

Set objDict1 = Nothing

Nothing is a special value, which can only be used with object variables. By setting an object variable to Nothing , you are basically saying 'I don't need this object reference anymore.' It is important to note that the two line of code we added to this script setting the two variables to Nothing are, in this specific example, unnecessary. They are unnecessary because, as we said, an object will be destroyed automatically when the reference variables naturally go out of scope. objDict1 and objDict2 go out of scope when the script ends, so in this short script the Nothing lines are not necessary.

However, it is important to use Nothing in longer scripts. This principle to keep in mind is that you do not want to have objects in memory any longer than you need them. Objects take up a relatively large amount of resources, so you want to instantiate them right before you need them and destroy them as soon as you don't need them anymore. This example script from Chapter 4 illustrates this principle ( FSO_FIND_FILE.VBS from Chapter 4).

Option Explicit Dim objFSO Dim objRootFolder Dim objFileLoop Dim boolFoundIt Set objFSO = _ WScript.CreateObject("Scripting.FileSystemObject") Set objRootFolder = objFSO.GetFolder("C:") Set objFSO = Nothing boolFoundIt = False For Each objFileLoop In objRootFolder.Files If UCase(objFileLoop.Name) = "AUTOEXEC.BAT" Then boolFoundIt = True Exit For End If Next Set objFileLoop = Nothing Set objRootFolder = Nothing If boolFoundIt Then MsgBox "We found your AUTOEXEC.BAT file in " & _ "the C: directory." Else MsgBox "We could not find AUTOEXEC.BAT in " & _ "the C: directory." End If

Take a look at this line in relation to the rest of the script.

Set objFSO = Nothing

The reason for this line is that at that point we don't need objFSO anymore. The only reason we needed it was to call the GetFolder method to get a Folder object. Once we have the Folder object, objFSO is of no more use to us in this script, so we follow the principle of limiting object lifetime as much as possible to objFSO to Nothing .

Tip  

For more information on the Nothing keyword, the Is Nothing statement, and the IsObject function, please see Chapter 3.

The Dictionary Object

In Chapter 3, we introduced the array, which is a unique data type that allows you to store multiple separate values in a single variable. The Dictionary object offers similar functionality, but in a different way. You may remember the phone list example from Chapter 3. The phone list was a simple two-dimensional array of names and phone numbers , as shown in the table below. The left column and top row show the subscripts of the array.

 

1

2

Williams

Tony

404-555-6328

1

Carter

Ron

305-555-2514

2

Davis

Miles

212-555-5314

3

Hancock

Herbie

616-555-6943

4

Shorter

Wayne

853-555-0060

One problem with this data structure is that there is not an easy way to search the array-for example, a search for a certain name by phone number or a certain phone number by name . One way would be to loop through the whole array and check the appropriate ' columns ' to see if we have found the 'row' we want. There are also other, more advanced ways to search this array, but they have to be programmed manually.

The Dictionary object solves this problem by providing an associative array , which means that each item (or 'row,' if it helps you to think about it that way) in the array has a unique key associated with it. So instead of having to search for an item in the dictionary, we can simply ask for it using the key. A Dictionary object can hold any type of data, from simple data type such as strings and dates to complex data types such as arrays and objects.

In this chapter, we will cover the basics of the Dictionary object, including an overview and examples. For a complete reference of the Dictionary object's properties and methods , please consult Appendix F.

Overview

Let's use our phone list example to show how things can be done differently using a Dictionary object instead of an array. In this example, we are going to get a little fancy and store arrays of phone list entries in a dictionary, and for each entry, we will use the phone number as the key. This will allow us to keep the list information structured (separated into last name, first name, and phone number) while also giving us the ability to find a row quickly using the phone number as the key.

Later, in Chapter 8, when we discuss the creation of your own VBScript classes, we'll extend our phone list yet again by using a custom PhoneEntry class for each entry instead of the array.

This code will populate the dictionary with our phone list ( DICT_FILL_LIST.VBS ).

Option Explicit Const LAST = 0 Const FIRST = 1 Const PHONE = 2 Dim dicPhoneList Set dicPhoneList = CreateObject("Scripting.Dictionary") FillPhoneList Sub FillPhoneList Dim strItemAdd(2,0) Dim strKey 'Populate the list, using phone number as the key. 'Add values to temp array, then add temp 'array to dictionary. strItemAdd(LAST, 0) = "Williams" strItemAdd(FIRST, 0) = "Tony" strItemAdd(PHONE, 0) = "404-555-6328" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Carter" strItemAdd(FIRST, 0) = "Ron" strItemAdd(PHONE, 0) = "305-555-2514" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Davis" strItemAdd(FIRST, 0) = "Miles" strItemAdd(PHONE, 0) = "212-555-5314" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Hancock" strItemAdd(FIRST, 0) = "Herbie" strItemAdd(PHONE, 0) = "616-555-6943" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Shorter" strItemAdd(FIRST, 0) = "Wayne" strItemAdd(PHONE, 0) = "853-555-0060" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd End Sub

First, we declare some named constants to make our array subscripts more clear. Then we declare a script-level variable called dicPhoneList for our Dictionary , which we instantiate using the CreateObject function. Then we call the FillPhoneList procedure, which populates the script-level Dictionary . For each list entry, FillPhoneList builds a simple array, which we declared as a local variable, sets the key using the phone number, and stores the entry in the dictionary.

Notice that the Add method takes two arguments. The first is the key for the item you wish to add and the second is the item value itself, in this case an array that holds one phone list entry, including last name, first name, and phone number.

Now let's extend this script to do something useful with our Dictionary object DICT_RETRIEVE_LIST.VBS ).

Option Explicit Const LAST = 0 Const FIRST = 1 Const PHONE = 2 Dim dicPhoneList Set dicPhoneList = CreateObject("Scripting.Dictionary") FillPhoneList SearchPhoneList Sub FillPhoneList Dim strItemAdd(2,0) Dim strKey 'Populate the list, using phone number as the key. 'Add values to temp array, then add temp 'array to dictionary. strItemAdd(LAST, 0) = "Williams" strItemAdd(FIRST, 0) = "Tony" strItemAdd(PHONE, 0) = "404-985-6328" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Carter" strItemAdd(FIRST, 0) = "Ron" strItemAdd(PHONE, 0) = "305-781-2514" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Davis" strItemAdd(FIRST, 0) = "Miles" strItemAdd(PHONE, 0) = "212-555-5314" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Hancock" strItemAdd(FIRST, 0) = "Herbie" strItemAdd(PHONE, 0) = "616-555-6943" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd strItemAdd(LAST, 0) = "Shorter" strItemAdd(FIRST, 0) = "Wayne" strItemAdd(PHONE, 0) = "853-555-0060" strKey = strItemAdd(PHONE, 0) dicPhoneList.Add strKey, strItemAdd End Sub Sub SearchPhoneList Dim strPhone Dim strItemRead strPhone = InputBox("Please enter a phone number " & _ "(XXX-XXX-XXXX) with which to search the list.") If dicPhoneList.Exists(strPhone) Then strItemRead = dicPhoneList.Item(strPhone) MsgBox "We found that entry:" & vbNewLine & _ vbNewLine & _ "Last: " & strItemRead(LAST,0) & vbNewLine & _ "First: "& strItemRead(FIRST,0) & vbNewLine & _ "Phone: " & strItemRead(PHONE,0) Else MsgBox "That number was not found in the " & _ "phone list." End If End Sub

We have added a new procedure called SearchPhoneList . This procedure asks the user for a phone number in the proper format, and checks the dictionary to see if there is an entry for that number. If there is, the code displays the entry in a dialog box. We use the Exists method to check to see if the number was used as a key value in the dictionary. Exists will return True if the key has a match in the dictionary, False if not. If Exists returns True , then the code can use the Item property with confidence to retrieve the phone list entry.

Notice that when we retrieve the array from the dictionary we put it into a variable ( strItemRead ) before we start using the array subscripts to get the values from inside the array. This is an optional technique, but one that makes your code a little easier to read. It's optional because VBScript can figure out for us that there is an array stored in the dictionary item without our having to feed the array into the strItemRead 'holding variable' first. The following alternative syntax achieves the exact same result without the extra variable.

If dicPhoneList.Exists(strPhone) Then With dicPhoneList MsgBox "We found that entry:" & vbNewLine & _ vbNewLine & _ "Last: " & .Item(strPhone)(LAST,0) & _ vbNewLine & _ "First: " & .Item(strPhone)(FIRST,0) & _ vbNewLine & _ "Phone: " & .Item(strPhone)(PHONE,0) End With Else MsgBox "That number was not found in the " & _ "phone list." End If

This is the key syntax in this example (Notice that the code at this point is inside of a With block.).

.Item(strPhone)(LAST,0)

Since we know in advance that there is an array stored in the dictionary, we can just follow the call to the Item property with the array subscripts we want. VBScript does the work behind the scenes. The 'holding variable' is optional. Different programmers will prefer one convention over the other, and you can choose which ever you prefer.

Three Different Ways to Add

Let's look at some additional syntactic variations that are possible with the Dictionary object. All of the following are valid ways to add a new item to a dictionary.

dicAnimals.Add "12345", "Cat" dicAnimals.Item("45678") = "Dog" dicAnimals("98765") = "Goldfish"

The first line is the syntax we've demonstrated already, using the Add method. This is the most explicit syntax, but somehow not as much fun as the methods used in the second or third lines. The second and third lines both take advantage of two particular behaviors of the Dictionary object: one, since Item is a property (as opposed to a method), you can bypass the Add method and just set the Item property directly using the equals operator ( = ); and the other, if you pass a key value to the Item property that is not found in the dictionary, the dictionary object will add that key to the dictionary behind the scenes.

Behavior number two makes possible the syntax in those second and third lines. However, this trick is a double-edged sword. This same behavior also holds true when you are simply reading from the Item property. If you use a line like the following to retrieve a value from a dictionary, and the key you pass in does not exist in the dictionary already, then you just added an empty item to the dictionary even though you probably did not intend to.

strAnimalName = dicAnimals.Item("72645")

That is why it is important to use the Exists method first to ensure that the key you are about to use is really in the dictionary.

If dicAnimals.Exists("72645") Then strAnimalName = dicAnimals.Item("72645") End If

Finally, going back to that third syntax of adding to a dictionary:

dicAnimals("98765") = "Goldfish"

What this syntax is taking advantage of is the fact that the Item property is the default property of the Dictionary object. When a property is an object's default property, referring to it by name is optional. The second syntax refers to it by name, the third takes the shortcut. All three of these syntactical conventions are valid and acceptable.

The CompareMode Property

The CompareMode property controls which 'mode' the dictionary's Item property will use when comparing key values for equality. The options are 'Text' ( 1; vbTextCompare; the default), 'Binary' ( 0; vbBinaryCompare ), and 'Database' ( 2; vbDatabaseCompare ).

The main thing you have to think about when it comes to the CompareMode property is whether or not you want your key comparisons in the Item method to be case sensitive. If case in sensitive is fine, then you can accept default value of vbTextCompare ( 1 ). If, on the other hand, you want comparisons to be case sensitive, change this property to vbBinaryCompare ( ). Take a look at the following code.

dicAnimals.Add "abc", "Cat" dicAnimals.Add "def", "Dog" dicAnimals.Add "ABC", "Goldfish"

If you use vbTextCompare , then the third line of this code will produce an error that you are trying to add a duplicate key to the dictionary. This will occur because with vbTextCompare "abc" and "ABC" are seen as equivalent. If, however, you use vbBinaryCompare , then the third line will not produce an error because "abc" and "ABC" are seen as distinct values.

The Item Property

We have seen the Item property in action in several of the earlier examples. Item is the gateway to the data storage inside the Dictionary object. To read from the Item property, you can access it directly with a particular key, or you can enumerate the Items collection with a For Each loop to access each dictionary item in order. Item can be used in three ways:

Whenever accessing the Item property directly (as opposed to indirectly with a For Each loop), you must pass the Key argument, which can be any unique string or integer. The key value determines which item in the dictionary will be written to or read from.

Following is an example syntax for the Item property.

'Add an item to the dictionary dicAnimals.Item("1234") = "Cat" 'Update an item already in the dictionary dicAnimals.Item("1234") = "Feline" 'Read an item from the dictionary strAnimalName = dicAnimals.Item("1234")

Item is the default property, which means that referring to it by name is optional.

The Exists Method

You can use the Exists method to check if a key is already in the dictionary. If you are not positive that the key is in the dictionary, it is important to use the Exists method before reading from the Item property. This is because the Item property will add the key if there is not a match. It is also wise to check the Exists method before calling the Remove method, since Remove will raise an error if the key does not exist in the dictionary.

In this example given previously in this chapter, we check Exists before accessing Item .

If dicAnimals.Exists("72645") Then strAnimalName = dicAnimals.Item("72645") End If

The FileSystemObject Library

The remainder of this chapter is dedicated to the FileSystemObject (FSO) library. This chapter does not contain a complete and detailed reference for all of the FSO objects, properties, and methods . Appendix F does, however, contain a complete reference. If you need quick lookup for a particular property or method, Appendix F is the place. As for this chapter, in the following sections we will start with an overview of the FSO library, after which we will demonstrate , including example code, some common tasks such as opening and reading a text file; writing to a text file; and creating and copying files and folders.

Why FileSystemObject?

Quite often scripts need the ability to create a file, read a file, find a file or folder, check for the existence of a certain drive, etc. For security reasons, none of this functionality is built into the core VBScript language. However, all of this functionality and more is available from the scripting runtime's FileSystemObject library. The FileSystemObject is a kind of 'master object' that serves as the access point for a family of objects. All of the objects in the FileSystemObject hierarchy work together to provide functionality for accessing and manipulating the Windows file system.

The FileSystemObject (FSO) objects are intended for use with the Windows Script Host, Active Server Pages, and other 'safe' hosts . By default, access to the FileSystemObject is blocked from scripts running inside of the Internet Explorer browser. These security settings can be changed to allow browser scripts to use the FileSystemObject , but it is not recommended that you do so. It is also not recommended that you ask your users to do so.

The FSO object model consists of the following objects and collections.

Object or Collection Description

FileSystemObject

This is the main, or 'root,' object. To use any of the FSO objects, you must first create a FileSystemObject, at which point you can use its properties and methods to perform various functions and access the other objects. Example properties and methods: CopyFile, CreateFolder, FileExists, Drives

Drive

A Drive object represents a logical or physical drive mapped on the host computer. The drive can be a hard disk, CD-ROM drive, or even a RAM drive. Example properties and methods: DriveLetter, AvailableSpace, RootFolder

Drives

A Drives Collection is a child of FileSystemObject and holds zero or more Drive objects. The only way to obtain a reference to a valid Drives object is through the FileSystemObject.Drives property. All drives are included, regardless of type. Removable-media drives do not need to have media inserted to appear. Drives has two properties: Count and Item

File

A File object represents a file that exists in a folder on a drive. There are two ways to obtain a File object: one is from the FileSystemObject.GetFile method and the other is from the Folder.Files collection. The File object is easily confused with the TextStream object, which represents a 'stream' of text going into or coming out of a file, but not the file itself. Example properties and methods: DateCreated, ParentFolder, Copy, Delete

Files

The Files collection is a child of the Folder object. The only way to obtain a valid Files object is through the Folder.Files property. Only has two properties: Count and Item

Folder

A Folder represents a folder on a drive. You can obtain a reference to a Folder object through the Drive.RootFolder and the CreateFolder, GetFolder, and GetSpecialFolder methods of FileSystemObject. Example properties and methods: IsRootFolder, ParentFolder, Drive, Files, SubFolders

Folders

The Folders collection stores a list of Folder objects. You can obtain a Folders object only through the Folder.SubFolders property. Only has two properties and one method: Count, Item, and Add. The Add method allows you to add a new subfolder (as a Folder object) to the collection

TextStream

A TextStream object represents a series of text, either being read from a file, written to a file, or going to or coming from the Windows 'standard i/o.' You can obtain a TextStream object via the File.OpenAsTextStream, Folder.CreateTextFile methods, as well as the CreateTextFile, OpenTextFile, and GetStandardStream methods of FileSystemObject. Internally, a TextStream object has a line pointer and a character pointer. When reading or writing a file as a TextStream, you move through the file from top to bottom only once , character-by-character and/or line-by-line . Example properties and methods: Read, Write, ReadLine, WriteLine, AtEndOfLine

Using Collections

The FileSystemObject hierarchy contains a few Collection type objects-a subject we have not yet discussed. What is a Collection ? A Collection is a special type of object much like the Dictionary object, in that it stores a key-indexed group of data. VBScript does not natively support generic Collection objects, but many COM-based libraries, such as the scripting runtime we've been discussing in this chapter, will use Collection objects.

There is no real mystery to a Collection if you understand the Dictionary . Like Dictionary , Collection has Count and Item properties, and you can iterate the Item property using a For Each loop. However, Collection does not have some of the niceties of the Dictionary such as the Exists and RemoveAll methods. You'll see examples of the syntax throughout the remainder of this chapter as we discuss FSO collections such as Drives , Folders , and Files .

Understanding FileSystemObject

The FSO objects are a little strange to some programmers at first because the root object, FileSystemObject , is the access point for everything else. Before you can do anything with drives or folders or files, you must first create a FileSystemObject . Either directly or indirectly, everything you want, you have to start by going through FileSystemObject . Take a look at the properties, and especially , methods of FileSystemObject and you will see all of the tasks it can perform and data it can provide.

The most important thing to keep in mind is that if you want access to any object other than FileSystemObject itself, then you have to get that object directly or indirectly through one or more of the properties and methods of FileSystemObject . Sometimes you have to 'drill down' through the levels of objects to get what you want, and at other times, FileSystemObject will have a method that does exactly what you need.

Let's look at two examples that accomplish the same task. Our goal is to locate the AUTOEXEC.BAT file and display the date and time it was last changed. This first example uses an indirect, drill-down methodology ( GET_AUTOEXEC_1.VBS ).

Option Explicit Dim objFSO Dim objCDrive Dim objRootFolder Dim objFileLoop Dim objAutoExecFile Set objFSO = CreateObject("Scripting.FileSystemObject") Set objCDrive = objFSO.GetDrive("C") Set objRootFolder = objCDrive.RootFolder For Each objFileLoop in objRootFolder.Files If UCase(objFileLoop.Name) = "AUTOEXEC.BAT" Then Set objAutoExecFile = objFileLoop Exit For End If Next If IsObject(objAutoExecFile) Then MsgBox "The autoexec.bat was last changed on: "& _ objAutoExecFile.DateLastModified Else MsgBox "Could not find autoexec.bat." End If

This code starts at the top of the hierarchy, drills into the drive level, then the folder level, then the file level-a lot of trouble to find one file, especially when we know right where the file should be. But there is a much simpler way to solve the same problem, one which takes advantage of the direct method provided by FileSystemObject ( GET_AUTOEXEC_2.VBS ).

Option Explicit Dim objFSO Dim objAutoExecFile Set objFSO = CreateObject("Scripting.FileSystemObject") Set objAutoExecFile = objFSO.GetFile("C:autoexec.bat") MsgBox "The autoexec.bat was last changed on: "& _ objAutoExecFile.DateLastModified

If you find yourself going to a lot of trouble to accomplish a task, take a step back, look through the various FSO objects, properties, and methods, and you might find a more direct way.

Creating a Folder

There are two different ways to create a folder. Which one you choose depends on what you are doing otherwise in your script. If you are already working with a Folder object that represents the folder in which you want to create the new folder, you can use the Folder.SubFolders.Add method, like so ( FSO_CREATE_FOLDER.VBS ):

Option Explicit Dim FSO Dim objFolder Set FSO = CreateObject("Scripting.FileSystemObject") Set objFolder = FSO.GetFolder("C:") If Not FSO.FolderExists("C:TestVBScriptFolder") Then objFolder.SubFolders.Add "TestVBScriptFolder" MsgBox "The C:TestVBScriptFolder folder was " & _ "created successfully." End If

Note  

if you run this script on your computer, you can run the FSO _ CLEANUP.VBS script to remove the folder that was created.

The above example, as mentioned, is the most practical if you already have a Folder object that you are working with. However, there is a quicker way to create a new folder if you don't otherwise have any need for a Folder object. The following code simply uses the FileSystemObject.CreateFolder method ( FSO_CREATE_FOLDER_QUICK.VBS ).

Option Explicit Dim FSO Set FSO = CreateObject("Scripting.FileSystemObject") FSO.CreateFolder("C:TestVBScriptFolder") MsgBox "The C:TestVBScriptFolder folder was " & _ "created successfully."

Note  

if you run this script on your computer, you can run the FSO _ CLEANUP.VBS script to remove the folder that was created.

This script accomplishes the same thing, but without using the Folder object. As mentioned previously, with FSO you can often perform the same task using one of FileSystemObject 's methods or one of the other FSO objects.

Copying a File

Copying a file is pretty simple. The FileSystemObject object has a method called CopyFile exactly for this purpose. The following script copies a file that is assumed to exist in the same directory as the script file ( FSO_COPY_FILE.VBS ). If you downloaded all of the code for this chapter in the same directory, the file we are copying should be there.

Option Explicit Dim FSO Set FSO = CreateObject("Scripting.FileSystemObject") If FSO.FileExists("TEST_INPUT_FILE.TXT") Then FSO.CopyFile "TEST_INPUT_FILE.TXT", _ "TEST_INPUT_FILE_COPY.TXT", True End If

Note  

if you run this script on your computer, you can run the FSO _ CLEANUP.VBS script to remove the file that was created.

Notice that we check FileExists first because if the file we are copying does not exist then CopyFile will raise an error. Also notice that we have passed True for the overwrite argument, which means that we want to overwrite the file if it already exists. If you did not want to overwrite it, you would want to use FileExists to check first.

The previous example assumes that the source and destination are both in the same directory as our script. You can also use full pathnames for both the file being copied and the target file.

FSO.CopyFile " A_Network_FolderCustomers.xls", _ "C:MyFolderSpreadsheetsCustomers.xls", True

You can also omit the filename from the target path if you want to use the same filename. In fact, omitting the filename is a requirement if you use wildcard characters in the source path:

FSO.CopyFile "A_Network_Folder*.xls", _ "C:MyFolderSpreadsheets", True

Notice the trailing backslash ('') that we have on the target path. This is critical because it signals the CopyFile method that Spreadsheets is a folder. Without the trailing backslash,

CopyFile will assume that Spreadsheets is a file we want it to create. Note also that wildcard characters are not allowed for the target path.

After you have copied a file, you can access it using either the FileSystemObject.GetFile method (which returns a File object) or find the file in the Folder.Files collection. Or, if the file you've copied is a text file and you need to read from or write to the file, you can open it as a TextStream (see the 'Reading a Text File' and 'Writing to a Text File' sections below).

Copying a Folder

Copying a folder is much like copying a file, but it's a bit more complex because a folder can contain multiple items, including subfolders and files. Also, you might want to copy more than one folder, in which case you may be able to use wildcard characters to identify the folders you want to copy. Here is a simple example:

Option Explicit Dim FSO Set FSO = CreateObject("Scripting.FileSystemObject") If FSO.FolderExists("C:TestVBScriptFolder") Then FSO.CopyFolder "C:TestVBScriptFolder", _ "C:Program Files", True End If

Note  

if you run this script on your computer, you can run the FSO _ CLEANUP.VBS script to remove the folder that was created.

Since we did not include any wildcard characters in the source path, the CopyFolder method assumes that TestVBScriptFolder is the one we want to copy, and it will look for a folder with exactly that name. If, however, we had wanted to copy any folders in the C: folder that start with the string TestVBScript , we could use wildcard characters.

FSO.CopyFolder "C:TestVBScript*", _ "C:Program Files", True

However, it is important to understand that the wildcard characters used with the CopyFolder method are only used to match folders-not files. If you want to copy some files and not others, then you must use the CopyFile method (see previous section).

You also have to be careful when copying folders with multiple files and subfolders into an existing hierarchy of identical folders and files. If any of those folders and files have the read-only attribute turned on, then the CopyFolder method will raise an error, even if you pass True for the overwrite argument. If you suspect that some target folders or files might have the read-only attribute turned on, you can use the Folder.Attributes and/or File.Attributes property to check first (see Appendix F).

Reading a Text File

It is often necessary to open a text file and either retrieve a specific piece of data from it, or simply feed the entire file contents into another data structure. We'll look at examples for both of these situations. Both of the scripts in this section assume that a file called TEST_INPUT_FILE.TXT exists in the same directory as the scripts. If you downloaded all of the code for this chapter, you should have everything you need.

You can see the contents of TEST_INPUT_FILE.TXT as follows . Each field is separated by a Tab character. Each line ends with a standard Windows end-of-line character-pair.

OrderID=456CustID=123ItemID=765 OrderID=345CustID=987ItemID=149 OrderID=207CustID=923ItemID=592

First, let's look at an example script that opens this file and looks for a specific piece of data ( FSO_READ_FILE_SEEK.VBS ). There is a lot of code here that has to do with parsing the data in the file, so if you want to focus on how we actually open and read the file, follow the objStream variable, which holds a TextStream object. The interesting part of this code is in the GetCustIDForOrder function, which opens a text file, searches for some particular data, and returns it.

Option Explicit Const ORDER_ID_TO_FIND = "345" Dim strCustID strCustID = "" strCustID = GetCustIDForOrder(ORDER_ID_TO_FIND) If strCustID <> "" Then MsgBox "The CustomerID for Order "& _ ORDER_ID_TO_FIND & " is: " & strCustID Else MsgBox "We could not find OrderID "& _ ORDER_ID_TO_FIND & "." End If Function GetCustIDForOrder(strOrderIDSeek) Const TristateFalse = 0 Const ForReading = 1 Const ORDER_FIELD = "OrderID=" Const CUST_FIELD = "CustID=" Const FILE_NAME = "TEST_INPUT_FILE.TXT" Dim FSO Dim objStream Dim strLine Dim lngFirstTab Dim lngSecondTab Dim strOrderID Dim strCustID strCustID = "" Set FSO = CreateObject("Scripting.FileSystemObject") If FSO.FileExists(FILE_NAME) Then Set objStream = FSO.OpenTextFile(FILE_NAME, _ ForReading, False, TristateFalse) Else MsgBox "Could not find "& FILE_NAME & "." GetCustIDForOrder = "" Exit Function End If Do While Not objStream.AtEndOfStream strLine = objStream.ReadLine lngFirstTab = InStr(Len(ORDER_FIELD), strLine, _ vbTab, vbBinaryCompare) strOrderID = Mid(strLine, Len(ORDER_FIELD) + 1, _ lngFirstTab - Len(ORDER_FIELD) - 1) If strOrderID = strOrderIDSeek Then lngSecondTab = InStr(lngFirstTab + 1, strLine, _ vbTab, vbBinaryCompare) strCustID = Mid(strLine, lngFirstTab + _ Len(CUST_FIELD) + 1, _ lngSecondTab - (lngFirstTab + _ Len(CUST_FIELD))) Exit Do End If Loop objStream.Close GetCustIDForOrder = strCustID End Function

After using FileExists to ensure that our input file is where we expect it to be, we use this line to open the file as a TextStream object:

Set objStream = FSO.OpenTextFile("TEST_INPUT_FILE.TXT", _ ForReading, False, TristateFalse)

We tell the OpenTextFile method which file we want to open, that we want to open it for reading (as opposed to writing), that we don't want to create it if it does not exist, and that we want to open it in ASCII mode. (Please see Appendix F for a detailed explanation of these arguments.) After this point, we have an open TextStream object in our objStream variable. The line pointer and the character position pointer are both at the beginning of the file. When processing a file, you have three options:

Which method you choose depends on what is in the file, how it is structured (if it is structured), and what you need to do with the data. Our example file is structured as a series of fields that repeat on a line-by-line basis. So we opted to use the ReadLine method:

Do While Not objStream.AtEndOfStream strLine = objStream.ReadLine ...<>... Loop objStream.Close

By setting up our Do loop this way, we ensure two things: one, that we only start the loop if the file we opened is not totally empty; and the other, that we will stop looping once we have read the last line of the file. The other thing that makes this work is that when we call the ReadLine method, the line pointer in objStream automatically moves ahead by one. A TextStream object always moves the pointers ahead automatically as you read the file. Finally, notice that we call the Close method on the TextStream object as soon as we are done with it; it's a good idea to call the Close method for any file you open.

We'll only comment briefly on these lines, which we omitted from the previous code snippet of the ReadLine loop.

lngFirstTab = InStr(Len(ORDER_FIELD), strLine, _ vbTab, vbBinaryCompare) strOrderID = Mid(strLine, Len(ORDER_FIELD) + 1, _ lngFirstTab - Len(ORDER_FIELD) - 1) If strOrderID = strOrderIDSeek Then lngSecondTab = InStr(lngFirstTab + 1, strLine, _ vbTab, vbBinaryCompare) strCustID = Mid(strLine, lngFirstTab + _ Len(CUST_FIELD) + 1, _ lngSecondTab - (lngFirstTab + _ Len(CUST_FIELD))) Exit Do End If

What we're doing here is using the InStr , Mid , and Len functions to parse the contents of each line, looking for specific known field markers. Other similar functions such as Left and Right are useful also, depending on the situation. You can learn about the details of how these functions work in Appendix A.

The particular techniques employed in this code depend on the fact that we know how the file should be structured. We know the field names and the fact that the field delimiter is the Tab character. For a production-quality script, we would also want to include some defensive code to ensure graceful handling of files that are not formatted as expected.

Writing to a Text File

Creating a new text file is about as straightforward as reading from one. The steps are simple: open a new text file in a specified location, write data to it, and close the file. All of this is done through the TextStream object. This simply demonstrates the three steps ( FSO_CREATE_FILE.VBS ):

Option Explicit Dim FSO Dim objStream Const TristateFalse = 0 Const FILE_NAME = "CREATE_FILE_TEST.TXT" Set FSO = CreateObject("Scripting.FileSystemObject") Set objStream = FSO.CreateTextFile(FILE_NAME, _ True, TristateFalse) With objStream .WriteLine "Test Line 1" .WriteLine "Test Line 2" .WriteLine "Test Line 3" .Close End With MsgBox "Successfully created "& FILE_NAME & "."

Note  

if you run this script on your computer, you can run the FSO _ CLEANUP.VBS script to remove the file that was created.

Because in this case we are creating a new, blank text file, we use the FileSystemObject. CreateTextFile method. We pass True for the overwrite argument so that if a file of the same name is already there it will be replaced by our new file. Then we use the TextStream.WriteLine method to add one line at a time to the file. We could have also used the Write method to add the data all at one or a little bit at a time (adding multiple lines at once if we liked ). The WriteBlankLines method is also available. Finally, we use the Close method to close the file.

Sometimes you need to write to a file that already exists rather than create a new one. Unfortunately , the TextStream object, as is, only supports two ways to write to an existing file: one, appending data to the end of an existing text file, and the other, starting at the beginning of an existing file, which throws out all existing data in the file. The following example demonstrates how to append to an existing file. As for the ability to start writing at the beginning (which unfortunately means you also have to throw out all existing data in the file), this is of course not that useful; you might as well just use CreateTextFile to open a new blank file.

So let's take a look at an example of appending to an existing file ( FSO_APPEND_FILE.VBS ). This script assumes that you have run FSO_CREATE_FILE.VBS first.

Option Explicit Dim FSO Dim objStream Const ForAppending = 8 Const TristateFalse = 0 Const FILE_NAME = "CREATE_FILE_TEST.TXT" Set FSO = CreateObject("Scripting.FileSystemObject") If Not FSO.FileExists(FILE_NAME) Then MsgBox "Could not find "& FILE_NAME & ". "& _ "Please run FSO_CREATE_FILE.VBS first." Else Set objStream = FSO.OpenTextFile(FILE_NAME, _ ForAppending, False, TristateFalse) With objStream .WriteLine "Appended Line 1" .WriteLine "Appended Line 2" .WriteLine "Appended Line 3" .Close End With MsgBox "Successfully appended to "& FILE_NAME & "." End If

This code is very similar to the code for creating a new text file. Instead of using CreateTextFile , we use OpenTextFile , passing it the ForAppending value for the iomode argument (see Appendix F). Then we use the WriteLine and Close methods just as before. Adding new data to the file is basically the same as for creating a new file, except that you are instead adding to the end of an existing file. In some cases, you might prefer to get all of the new data into a single string variable and passing to the Write method.

There are two common writing- related tasks that the TextStream object does not support: first, inserting data at the beginning of an existing file, and second, adding data somewhere in the middle of an existing file. To accomplish these, you have to write some code of your own to open the file for reading, get the contents into one or more variables , close the file, add or remove whatever data you need, and then write it all back as a new file.

 

 

Summary

We started this chapter with an overview of the basic syntax and techniques for using objects in VBScript. This overview included an explanation of the CreateObject function, object lifetime reference counts, and object destruction.

We also introduced in this chapter the 'scripting runtime objects,' which is a set of objects that exist in a separate library apart from core VBScript, but which are nonetheless available almost anywhere VBScript can be hosted. The scripting runtime is divided into the Dictionary object and the FileSystemObject , which acts as the gateway to family of related objects. The focus in this chapter is to explain why these objects are useful, how they are designed, and how to perform common tasks . For a complete reference of the scripting runtime objects, please consult Appendix F.

The Dictionary object provides an associated array, allowing you to store a series of data of any type: strings, numbers , arrays, objects, etc. Each item added to a Dictionary object must be given a key value, which must be unique within the dictionary. The key can then be used to retrieve the item from the dictionary later.

The FileSystemObject library offers a variety of objects that allow you to interact with the Windows file system. Features include: creating and editing text files; creating folders; copying, moving, and deleting files and folders; 'drilling down' into the hierarchy of drives , folders, subfolders , and files; reading file attributes; and more.

 

 

Chapter 8 Classes in VBScript (Writing Your Own COM Objects)

Категории