For...NextI 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 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
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
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 InformationOur 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 InformationThe 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
Note
Worker and Output InformationThe 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
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
|