Previous Page
Next Page

For...Next

I know what you're thinking: "We just got finished looking at For...Next!" Well, sort of, but not really. An important difference between For Each...Next and For...Next is that with For Each...Next, you don't have to know how many times you want to do something. With the For...Next construct, you must know exactly how many times you want to do something.

Just the Steps

To implement For...Next

1.
On a new line in the script, type i followed by a variable and a count (such as For i = 1 to 10).

2.
On the next line, type the command to be performed.

3.
On the next line, type Next.


Using For...Next is not necessarily a bad thing, however, because it gives you a lot of extra control. For example, the DisplayProcessInformation.vbs script checks a number of performance indicators on the server (that is, process thread counts, page faults, working set sizes, and the like).

Warning

Make sure you run the DisplayProcessInformation.vbs script under CScript, or you will find yourself clicking an incredible number of dialogue boxes. To launch it under CScript, remember to go to a CMD prompt, and type cscript DisplayProcessInformation.vbs.


The values for these items can change quite often, so you want to check them on a regular basis. However, frequent checking can cause a performance hit on either the server or the network (depending on how the script was utilized), so you want to check the status only at certain times. The solution here is to take measurements of all the running processes, then wait an hour and do it again. You do this for an eight-hour cycle and then quit. You could use this type of script to monitor performance on a server that was heavily used during working hours.

Important

In many of the scripts, you will note that I have On Error Resume Next commented out. This is due to the "best practice" of having it turned off during development. You want to see all errors while you are writing the script, so you can fix any problems that may arise.


DisplayProcessInformation.vbs

Option Explicit
'On Error Resume Next
Dim objWMIService
Dim objItem
Dim i
Const MAX_LOOPS = 8, ONE_HOUR = 3600000

For i = 1 To MAX_LOOPS
Set objWMIService = GetObject("winmgmts:").ExecQuery _
  ("SELECT * FROM Win32_Process where processID <> 0")
WScript.Echo "There are " & objWMIService.count &_
 " processes running " & Now
 For Each objItem In objWMIService
    WScript.Echo "Process: " & objItem.Name
    WScript.Echo Space(9) & objItem.commandline
    WScript.Echo "Process ID: " & objItem.ProcessID
    WScript.Echo "Thread Count: " & objItem.ThreadCount
    WScript.Echo "Page File Size: " & objItem.PageFileUsage
    WScript.Echo "Page Faults: " & objItem.PageFaults
    WScript.Echo "Working Set Size: " & objItem.WorkingSetSize
    WScript.Echo vbNewLine
  Next
  WScript.Echo "******PASS COMPLETE**********"
  WScript.Sleep ONE_HOUR
Next

Header Information

Our Header information section begins with the Option Explicit command that tells VBScript that all our variables will have to be formally announced by using the Dim command. One issue to keep in mind about Option Explicit is that it must be the first non-commented line in the script. For instance, in the electronic version of the next script (found on the companion CD), notice that several lines have been commented out by using the single quotation mark character ('). These lines are used to tell basic information about the purpose of the script, provide documentation on the use of various variables, and explain some of the syntax peculiarities.

Once all that work is done, the first line without a single quotation mark must be Option Explicit if you want Option Explicit to work. The reason for this is that when the line without the single quotation mark is not the first line in the script, some variables can sneak in without being declared. On Error Resume Next uses the second line in our script. As you no doubt have noticed, On Error Resume Next and Option Explicit seem to appear in all scripts. If you were going to create a template for script creation, Option Explicit and On Error Resume Next would be a couple of good lines to include, because more than likely you'll want them in all your scripts. However, you might want to comment out the On Error Resume Next line by placing a single quotation mark in front of it. In this way, while you are writing and testing your script, you will be able to catch all the errors, because On Error Resume Next is turned off. Once testing is completed, you simply remove the single quotation mark from in front of On Error Resume Next, turning it back on. This has the advantage of hiding unexpected errors from the "end user" of the script once it moves from development to "production."

This script has only three variables: objWMIService, which is used to hold the connection to WMI, allowing you to query for performance information about the running processes; objItem, which is used to hold the name of each process that comes back from objWMIService; and lastly i, which is one of the weird little variables used to increment the For...Next loop. Because i is, however, a variable, and you turned on Option Explicit, you need to declare it by using the Dim command.

Reference Information

The Reference information section of the DisplayProcessInformation.vbs script contains two constants: MAX_LOOPS and ONE_HOUR. MAX_LOOPS is used by the For...Next statement to control how many times the script will execute. On this line in the script, we have done something new: We put two constant statements on the same line, just like you can do when you dim variables. This is seen here:

Const MAX_LOOPS = 8, ONE_HOUR = 3600000

This is the same as doing the following (the advantage is that it saves space):

Const MAX_LOOPS = 8
CONST ONE_HOUR = 3600000

You define a constant named ONE_HOUR and set it equal to 3,600,000. You're going to use this constant in conjunction with the Sleep command, which counts in milliseconds. To calculate, you'd multiply 60 minutes by 60 seconds, and then multiply the result by 1,000, which yields 3,600,000. By defining the ONE_HOUR constant, you make the script easier to read. In addition, you might want to add several other constants in the script, such as HALF_HOUR, QUARTER_HOUR, and FIVE_MINUTES, and then you could easily change the sleep timeout value later in the script. Defining constants but not using them in the script doesn't adversely affect the running of the script, because you comment them to that effect.

Adding additional constants

1.
Open Microsoft Notepad.

2.
From the File menu, choose Open. In the Files Of Type box, choose All Files from the drop-down list.

3.
Navigate to My Documents\Microsoft Press\VBScriptSBS\Ch02\.

4.
Select DisplayProcessInformation.vbs, and choose Open from the Action menu.

5.
In the Reference section of the script, locate the following line:

Const MAX_LOOPS = 8, ONE_HOUR = 3600000

6.
Add a new line under the two existing constant definitions.

7.
Add a constant for half an hour. It will look like the following:

Const HALF_HOUR = 1800000

8.
At the end of the statement, add a comment such as the following:

'30 minutes in milliseconds.

9.
Do the same thing for fifteen minutes, and for five minutes. The completed section will look like the following:

Const QUARTER_HOUR = 900000 'fifteen minutes in milliseconds
Const FIVE_MINUTES = 300000 'five minutes in milliseconds

10.
Save your work and compare the results with DisplayProcessInformationExtraConstants.vbs.

11.
Change the WScript.sleep command to use the FIVE_MINUTES constant at the bottom of the script. It will look like the following:

WScript.Sleep FIVE_MINUTES

12.
Save and run the script using CScript from a CMD prompt. Time the execution. It should make a second pass after five minutes. Compare it with the DisplayProcessInformationFiveMinutes.vbs script.

13.
Use Calc.exe to come up with additional constants to use for this script (such as one minute. The formula is n(minutes) * 60(seconds) * 1000(for milliseconds).

Note

Notice the underscore (_) that appears at the end of the first and second lines in the Worker information section. This is used to break up the code into more than one line to make the code easier to read. The important aspect to pay attention to is the placement of the open and close parentheses and the quotation marks (" ") as you break up the lines. Notice also that at times, the ampersand is used, which as you'll recall from Chapter 1 is the concatenation character. This ampersand is used when you're inside of the parentheses, and you need to stick the two lines together. At times, you'll need to embed spaces to ensure commands are not messed up when you break the lines. The line continuation following ExecQuery does not include the ampersand because it falls outside of the parentheses.


Worker and Output Information

The Worker section of the script consists of a rather nasty WMI query string and its attendant assignment to the objWMIService variable. The nasty code is shown here:

Set objWMIService = GetObject("winmgmts:").ExecQuery _
  ("SELECT * FROM Win32_Process where processID <> 0")

This line of code connects to WMI and then executes a query that lists all Win32 processes running on the machine. You'll learn about WMI in Chapter 8, but for now, it is important to notice that the query looks exactly like a regular SQL Server query. The code says to select (which means to choose something) from the Win32 process. The "something" that is being chosen is *. As you no doubt recognize, * is the wildcard character and means "everything." So this query chooses everything from the Win32 process, but only if the process ID is not equal to 0 (the system idle process).

As we continue, the Worker and the Output information sections kind of merge together. This section begins with the For i = 1 To MAX_LOOPS command, which means that you're going to count to eight and on each pass increment the value of the variable i. With each pass, the variable i changes its value. In the second line of the Worker information section is a For Each...Next command. This tells you that the information returned from the objWMIService variable is a collection. Because it is a collection, you need to use For Each...Next to walk (iterate) through the collection. As the code walks, it echoes out the value of the information you want (such as the process, process ID, and thread count). At the end of the grouping of WScript.Echo commands is a Next command. The problem with nested Next commands is trying to keep track of which Next belongs to which For or For Each. Indenting them a little bit will help you see which For command lines up with which Next command. This technique makes the script easier to read.

The Now command is used to echo out the date and time, providing an easy way to time stamp logs and other output obtained from scripts. In addition, because Now is inside the For Each...Next loop, it will time stamp each process as it is reported. This enables you to see how long it takes the script to complete its processingthe Now command reports down to the second.

The Space () command uses the space function that is built into VBScript. We do not need to define it, or declare it, or anything. It is built into the scripting language. It acts like a variable tab command, in that we can tell it how many spaces we want, and it magically skips over that many spaces.

The vbNewLine command is really a constant value that is built into VBScript. It tells the script to print out a new line for us.

Using the Space function and the vbNewLine constant

1.
Open Notepad or the script editor of your choice.

2.
On the first line of your script, set Option Explicit, as seen below:

Option Explicit

3.
On the next line, use WScript.Echo and the Space() function at the beginning of the line to jump over 10 spaces. Follow the command with some text indicating the space. It may look like the following:

WScript.Echo Space(10) & "this is a 10 space line at the beginning"

4.
On the next line, WScript.Echo some text with the vbNewLine constant at the end of your line. Your code could look like the following:

WScript.Echo "This line ends with vbNewLine" & vbNewLine

5.
Now let's end by using the Space function embedded in a line of text. Note that we will need to use the & concatenation character before and after the function, as seen below:

WScript.Echo "This is an embedded 5 spaces" & Space(5) & "in the line"

6.
Save and run your script. If you have problems, you can compare your code with the SpaceAndVBNewLine.vbs script in My Documents\Microsoft Press\VBScriptSBS\Ch02.

The WScript.Sleep command is used to pause the execution of the script for a specified amount of time. As mentioned earlier in this chapter, the Sleep command takes its input in the form of milliseconds. To pause the script for one second, you would write the code like this:

WScript.Sleep 1000

I've been calling this the Sleep command, but in programming speak it would be called the Sleep method of the WScript object. However, if I called it that, this book would sound like a programmer's reference and therefore would be boring. So I'll just call it the Sleep command and be done with it.

Pausing the script can have a number of uses. For instance, it enables you to have a very flexible running schedule. If you attempted to pause the script using the scheduler service on Windows Server 2003, you would need eight different schedules, because there is no notion of "pause for an hour, and only do it for eight hours." One other very useful aspect of the Sleep command is that it allows for "spin-up time." By using the Sleep command, you can cause a script to wait for a slower component to come on line prior to execution. The Sleep command is not an atomic clock. Although it's fine for generic pausing of a script, don't think you can use it for scientific timingit was never designed for that purpose. In general, it's not accurate for periods of time less than a second.

We use WScript.Echo to indicate that the script has finished its pass through the processes. Remember that anything inside the quotation marks will be echoed to the screen. By padding the script with a bunch of *****, you can more easily find your information. The other important thing to notice here is that each time we make a loop with the For...Next statement, we are re-issuing the WMI query. At first glance, this may seem ineffecient. However, if we did not do the query a new time for each loop, then we would just be printing out the results of the first query eight times with no new resulting information, which would be even less efficient!

For i = 1 To MAX_LOOPS
Set objWMIService = GetObject("winmgmts:").ExecQuery _
  ("SELECT * FROM Win32_Process where processID <> 0")
WScript.Echo "There are " & objWMIService.count &_
 " processes running " & Now
 For Each objItem In objWMIService
    WScript.Echo "Process: " & objItem.Name
    WScript.Echo Space(9) & objItem.commandline
    WScript.Echo "Process ID: " & objItem.ProcessID
    WScript.Echo "Thread Count: " & objItem.ThreadCount
    WScript.Echo "Page File Size: " & objItem.PageFileUsage
    WScript.Echo "Page Faults: " & objItem.PageFaults
    WScript.Echo "Working Set Size: " & objItem.WorkingSetSize
    WScript.Echo vbNewLine
  Next
  WScript.Echo "******PASS COMPLETE**********"
  WScript.Sleep ONE_HOUR
Next

Quick Check

Q.

WScript.Sleep is expressed in what unit?

A.

WScript.Sleep is expressed in milliseconds.

Q.

What is an important difference between For Each...Next and For...Next?

A.

With For Each...Next, you don't need to know the number of elements in advance.



Previous Page
Next Page