Have you ever needed to run a command against 2000 workstations and thought, "Man that batch file is going to run forever!" You might even worry the data will be too stale to matter when it finally gets to you. I’ve run into this situation multiple times and the fix was to write a quick and dirty app in .NET that uses some simple multithreading. I just change around some strings, recompile, and I’m off to the races. Not a programmer you say? Well, you need to learn a little. And if .NET isn’t your thing, learn some VBScript and PowerShell. Next year I plan to start abandoning VBScript <sniff> and moving to PowerShell, but because PowerSehll is not standard on all Windows systems yet it is easier to write i VBScript. So what I provide for you here is a simple VBScript that will run multiple processes concurrently. This is not multti-threading, so it uses more resources than my .NET code does, but it’s pretty darn cool.
So let’s start with the main piece the Class we create to handle our new process.
Private Sub Class_Initialize()
Set p_Shell = CreateObject("WScript.Shell")
p_Exec = Empty
p_Available = "True"
EC_Retrieved = ""
Public Sub Open(cmd)
Set p_Exec = p_Shell.Exec(cmd)
PID = p_Exec.ProcessID
p_Available = "False"
p_ExitCode = ""
EC_Retrieved = "False"
Public Property Let Name(tempname)
p_Name = tempname
Public Property Get Name
Name = p_name
Public Property Get Available
If Not IsEmpty(p_Exec) Then
If p_Exec.Status <> 0 Then
p_Available = "True"
p_ExitCode = p_Exec.ExitCode
Available = p_Available
Public Property Get ExitCode
ExitCode = p_ExitCode
EC_Retrieved = "True"
The class has the constructor, one method, and 5 properties. Next we look at how I used them.
First we set up a sample array of computers to test against. This can be easily read in from Active Directory, a text file, or database table. Then we create an array to store new instances of our RunCmd class. In VBScript we cannot use the For Each…Next statement to populate each array element with a new instance of our class, me must use the standard For…Next which requires us to specify the number of items in our array.
Computers = Array("comp1","comp2","comp3","comp4","comp5","comp6","comp7")
for i = 0 to UBound(RunningProcesses)
Set RunningProcesses(i) = New RunCmd
After the array of RunCmd objetcs is set up it’s time to move to the main loop where the work is started. StartProcess is called, which verifies the RunCmd object stored in that array element is not currently running an active process and if it is not it starts a new process to connect to the current computer fom the Computers array. Once the process is started we move to the CheckProcess function which checks the number of objects in the RunningProcesses array have active processes running. For the example I have set the number to 3. So once 3 active processes have been reached it will loop until less than 3 are running. As the CheckProcesses it calls it will retrieve data from the objects that have data. So far the only thing I am capturing os the ExitCode. I have not attempted to capture the StdOut as normally VBScript will wait for the process to finish, and we are try to avoid that!
For Each computer in computers
Do While CheckProcesses() >= 3
Once we have looped througn all of the computer we want to connect to we could still have processes in play, so we continue to run CheckProcesses while until we have 0 processes running.
Do While CheckProcesses() > 0
Here is the code for the StartProcess subroutine. We pass the name of the computer we are attempting to connect to then we pass a command to to the Open method of object p. When the method Open runs it assigns the PID of the process we started to the PID property of the object.
For Each p in RunningProcesses
If p.Available = "True" Then
p.open("cmd /c sc \\" & cname & " query snare|find /i ""_name""")
p.Name = cname
WScript.StdOut.WriteLine "Process Started: " & p.pid
The CheckProcess function is where we determine how many processes are running and grab the exit codes of completed processes. When we get the Available property it check to see if the p_Exec object inside of our RunCmd object has a status other than 0, which means it has completed. If it has not completed it increments the ProcessCount variable. If it has completed it checks the EC_Retrieved property to see if we have already retrieved the ExitCode of the process. If we have not retrieve it yet access the ExitCode property. When we request this property it turns the EC_Retrieved property flag to True. You can then code for whatever the expected error code might be.
ProcessCount = 0
For Each p in RunningProcesses
If p.Available = "False" Then
ProcessCount = ProcessCount + 1
If p.EC_Retrieved = "False" Then
If p.ExitCode = "1" Then
WScript.StdOut.WriteLine p.Name & " – Snare is not installed."
ElseIf p.ExitCode = "0" Then
WScript.StdOut.WriteLine p.Name & " – Snare is installed."
CheckProcesses = ProcessCount
There’s a lot of application for this code, and it’s a fun tutorial (I think) for creating your own classes in VBScript. Making your own class is a powerful tool and it can be used in tons of different applications. Feel free to post a comment if you have questions or you’d like more examples!
EDIT: I have posted the complete code in the VBScripts download section!