Here are more scripts like the ones I presented in "Short, Sharp Scripts," January 2002, InstantDoc ID 23366. I've used this mixed bag of scripts to perform a variety of tasks over the past month: killing programs, performing folder recursion, retrieving details about all the printers attached to a PC, retrieving a list of Microsoft SQL Server systems in a domain, and starting and stopping FTP sites. You might find these scripts useful for your scripting toolkit.
Some of the scripts use freeware ActiveX COM components that you need to download to run the scripts. These components sometimes offer more functionality than I use, so check out the URLs that I provide to find documentation that can help you expand and enhance the scripts.
For each script, the computer running the script is the source, and the system that the script connects to is the target. When the source and target are the same computer, you need to run the script locally on that machine.
Killing a Program
One useful tool is a script that terminates unwanted programs. You might need to kill all instances of a particular program because a fault has occurred, or during a file-copy operation, you might need to kill a program that has the file open. In such cases, you can open Task Manager and manually kill one or more processes, but you might want an automated solution when you need to terminate many processes.
Listing 1, page 2, shows KillingProcesses.vbs, which uses Windows Management Instrumentation (WMI) to iterate a client's processes, then kill each one. The script uses the WMI programmatic identifier (ProgID), the WMI connection string, and the WMI Query Language (WQL) Select statement. KillingProcesses.vbs terminates the processes by using a simple For Each...Next loop and the Win32_Process::Terminate method.
To use the script, replace TESTPC with the name of your target system and THEAPPTOKILL.EXE with the name of the application that you want to terminate on the target. You can run the script on any system that can access the target over the network. The account you use to run the script must have administrative privileges on the target system.
Web Listing 1, which you can download from http://www.winscriptingsolutions.com, InstantDoc ID 26024, contains the same code as Listing 1, but I've placed the code inside a subprocedure to show you how to build subprocedures for these code fragments should you choose to do so. You should pass the same two parameters to the subprocedure that I told you to replace in Listing 1. If you type in rather than download the scripts in Listing 1 and Web Listing 1, be sure to include a single quote (') before and after the name of the application to kill and a double quote (") before and after the entire Select string.
The RecurseFolders subprocedure, which Listing 2 shows, is quite useful when you need to iterate through every branch and leaf of a directory hierarchy. This subprocedure also provides a good example of how to use recursion in your scripts. Recursion is a procedure's or program's ability to call itself. In this case, a subprocedure that examines subfolders can call itself to force recursion on an entire directory tree.
The RecurseFolders subprocedure takes as input a string that contains the path to the folder from which you want recursion to start (e.g., "C:\windows"). RecurseFolders then uses the variable fso to store a newly created Scripting::FileSystemObject object. The subprocedure then uses the FileSystemObject::GetFolder method to connect to a Folder object that represents the folder whose path you passed to the subprocedure as input. After the subprocedure has connected to the Folder object, it can use the Folder::SubFolders method to retrieve a collection of the subfolders in that folder. The subprocedure then proceeds to the recursive loop. Using a For Each...Next loop, the subprocedure iterates through each Folder object in the collection; the body of the loop calls the RecurseFolders subprocedure again and passes it the path of the subfolder that's currently selected from the collection. After the subprocedure has traversed a particular branch, or if a particular folder has no subfolders, the subprocedure moves on to the Do something here section. Simply replace the commented lines with whatever action you want the subprocedure to take with each folder. For example, if you uncomment the second commented line, the subprocedure displays the name of the folder on screen. If you decide to uncomment this line, I suggest that you use cscript.exe to run the script or use a small folder structure so that you don't need to click OK in thousands of boxes when the subprocedure displays all the subfolder paths in the tree.
Retrieving Printers and Mapped Drives
The script that Listing 3 shows is a simple snippet that runs on the target machine (the source and target computers are the same for this script). This script uses the WScript::Network object to access the target's printer connections and network drives. The For...Next loops use the variable intCount to iterate systematically through the retrieved collection, starting at 0 and continuing to 1 less than the maximum number of printers or drives. However, in each case, the results come in pairs, so you need to operate on both intCount and intCount+1 to retrieve both results. In the case of printers, the script retrieves the printer name and Universal Naming Convention (UNC) path; in the case of drives, the script retrieves the drive letter (if one exists; otherwise this property is blank) and the drive's UNC path. Just replace the Do something comments with whatever you need to do to the two items. The two lines that follow the Do something comment lines in each case just display the results to the screen, but watching the results is a good way to learn about the script.
Listing SQL Server Systems
One thing that I needed to do this month was get a list of all the SQL Server machines running in a domain. DBAs can do this sort of thing in a jiffy with their administrative tools, but a DBA is never around when you need one. Rather than create a component myself, I searched the Web and soon found what I needed on The Code Project Web site at http://www.codetools.com/useritems/listsqlservers.asp. If you download the .zip file from http://www.codetools.com/useritems/listsqlservers/listsqlservers_src.zip, you can unzip and extract the sqlutils.dll file, which is the only file that you need for this script. You register the file in the usual way by using the command
Be sure to replace the sample path with the correct path to the DLL on your system.
Listing 4 shows the FindSQLServers function, which scans the network for the available servers and populates an array with the results. (It's a function rather than a subprocedure because it returns a result.) The function begins by initializing the index that it will use to build the array. Then, the function sets the objSqlUtils variable, which stores the SQLUtils::ListServers object. VBScript's CreateObject function instantiates the object from the sqlutils.dll file. The function then uses the ListServers::Domain property to specify the domain to scan. You must have access to the domain from the PC on which you run the script for this script to work. The function then calls the ListServers::GetSQLServers method to retrieve the list of available SQL Server systems in the specified domain.
The objSqlUtils variable stores the server list for you so that you can use the ListServers::Servers method to retrieve a server name as needed. The function could just use a For Each...Next loop to retrieve the names one by one. However, because FindSQLServers is a function, it must return the full list of servers—hence, the need for the array.
The function's second line dimensions the array. Each time the function processes another SQL Server system in the list, VBScript's ReDim statement redimensions the array by adding one element. The For Each...Next loop goes through each server in turn and adds it to the arrSQLUtils array. After adding all the servers to the array, the function returns the array as its result.
I've left some test code in the function that displays the results. You can remove this four-line code segment when you're happy with the way the script works.
Automating IIS FTP Sites
The AutomateIISFTPService function that Listing 5, page 4, shows makes use of the ability to use a ProgID and a path to connect to a Microsoft IIS system's FTP service and to an individual FTP site within the FTP service. The function lets you start or stop a particular FTP site instead of starting or stopping the whole FTP service, which starts or stops all FTP sites.
AutomateIISFTPService takes three parameters. The first parameter is a string variable that contains the word start or stop. The second parameter is the name of the IIS server that hosts the FTP service. The third parameter is the FTP site name. An example of how you might call the function is as follows:
The function can return one of three integers: 0 indicates a successful attempt to start or stop the FTP service, 1 indicates a failure to find the FTP service, and 2 indicates that the start or stop command was incorrectly spelled. Note that this function has minimal error trapping, so feel free to add some of your own.
For example, you could make sure that the start or stop actually succeeded rather than only that it was attempted. You could also add error-checking code by using the examples in "Trapping and Handling Errors in Your Scripts," May 2001, InstantDoc ID 20500.
The function begins by using VBScript's LCase function to change the strCommand variable to lowercase text, then checks to ensure that the variable contains the string "start" or "stop". If the variable doesn't match one of these strings, the function exits after returning a value of 2.
If the variable does match one of the strings, the function next uses VBScript's GetObject function with a path to bind to the IIS service running on the server that the strSvr parameter specifies. The path starts with the string "IIS://", which is the ProgID for the IIS service. The path continues with the name of the server, which strSvr contains, and ends with a string that contains "/MSFTPSVC", which tells IIS that the function wants to talk to the Microsoft FTP service. The Set command on the same line passes to the ftpService object (which is an instance of the IISFTPService object) a reference to the FTP service.
The function uses a For Each...Next loop to iterate through a collection of all the individual FTP sites (which are instances of the IISFTPServer object) that the FTP service hosts. All the sites that you might want to start or stop have numeric names in addition to their commonly used text names. Thus, the function uses an If...Then statement to first check that a site's name is numeric. Then, because you specified the site's external text name in the strSite parameter, the code must check each site's IISFTPServer::ServerComment property for that name. When the names in the strSite parameter and ServerComment property match, the code has found the site it's looking for. (VBScript's LCase function converts both names to lowercase.)
Now that the function has identified the numeric name of the FTP site, the script can add that name (from the Name property of the ftpSite object) to the string that it passed to GetObject earlier. The GetObject call returns an object that references the target FTP site. The code then checks the value of strCommand and uses the IISFTPServer::Start or IISFTPServer::Stop method on the target FTP site.
If you want to be really clever, you can replace the entire If...Then...Else statement at callout A in Listing 5 with the following line of text:
This code makes use of the fact that strCommand holds the name of the method that you want and lets you append it to a string and use VBScript's Execute statement to execute the text string as if you had told it to execute ftpTargetSite.Stop or ftpTargetSite.Start, as in callout A.
For more information about programmatically manipulating IIS, go to the Microsoft Developer Network (MSDN) Library at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/iisref/html/psdk/asp/aore.50h1.asp, where you'll find three links to the information you need.
That's all I have time for this month. I hope you find these code snippets useful and can extend and enhance the subprocedures and functions to deliver some useful solutions for your environment.