Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I use one script to monitor for the creation of four or five different processes?

-- BC

SpacerHey, Scripting Guy! AnswerScript Center

Hey, BC. You know, it’s too bad you didn’t ask how you could monitor four or five TV shows at the same time. Had you done that we would have had the perfect answer for you: get yourself a teenaged son. Although the Scripting Dad is rarely impressed by anyone or anything, he does stand in awe of the Scripting Son’s ability to watch two football games, Baseball Tonight, one movie, and whatever happens to be on Cartoon Network, all at the same time and all without missing a beat. The Scripting Dad, meanwhile, finds himself totally lost within seconds. “I thought Houston already played today,” he’ll say.

“Uh, Dad, that’s my video game.”

Oh. Right.

There’s no doubt that the Scripting Son – and his friends – possess an amazing talent; however, that probably won’t help you much, BC. After all, monitoring multiple processes sounds an awful lot like work, a word you rarely see used in the same sentence as “teenaged son.” That means you’ll have to make do with a script similar to this one:

arrProcesses = Array("freecell.exe","sol.exe","spider.exe","winmine.exe")

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

i = 0

Set colMonitoredProcesses = objWMIService. ExecNotificationQuery _        
    ("Select * From __InstanceCreationEvent Within 5 Where TargetInstance ISA 'Win32_Process'")

Do While i = 0
    Set objLatestProcess = colMonitoredProcesses.NextEvent
    strProcess = LCase(objLatestProcess.TargetInstance.Name)

    For Each strName in arrProcesses
        If strName = strProcess Then
            Wscript.Echo strName & " has started."
        End If
    Next
Loop

Before we talk about the script we should note that there were a couple different ways we could have attacked this problem. Our original thought was to write a complicated event query that would retrieve information for a only a specific set of processes. That sounded an awful lot like work, however, a word you rarely see used in the same sentence as “the Scripting Guy who writes this column.” Because of that, we opted for a much simpler approach: we get information about all the processes, then check each to see if the process is in our target list. It might not be as elegant, but it’s much easier and – because you have relatively few processes running on a computer – is nice and fast as well.

As for the script itself, it starts out by defining an array named arrProcesses and then adding four items to that array:

arrProcesses = Array("freecell.exe","sol.exe","spider.exe","winmine.exe")

And yes, those are the four processes we’re interested in.

With the array defined we connect to the WMI service on the local computer (although this script can also run against remote computers) and then assign the value 0 to a counter variable named i. That brings us to our event query:

Set colMonitoredProcesses = objWMIService. ExecNotificationQuery _        
    ("Select * From __InstanceCreationEvent Within 5 Where TargetInstance ISA 'Win32_Process'")

What we’re doing here is asking WMI to stop every 5 seconds (that’s what the Within 5 is for) and see if there are any new instances of the __InstanceCreationEvent class; new instances of this class are automatically created any time a new something – a process, a service, an event log entry, anything WMI knows about – is created. Of course, we aren’t interested in new services or new event log entries; the only thing we care about are new processes. That’s why we tack on the Where clause that limits returned data to instances of the Win32_Process class.

Note. Yes, we know: if you’re new to WMI events that’s a very cursory explanation. But don’t worry; you can always take a look at the Scripting Week 2 webcast An Ounce of Prevention: An Introduction to WMI Events if you need more information about writing WMI event scripts.

After issuing our query we next set up a Do While loop that’s designed to run until the variable i is equal to 1. Of course, we’ve designed the script so that i will never be equal to 1; that way the script will run forever and ever, or at least until you shut down the computer or kill the script process. We do that so that you can be notified any time one of the target processes is created.

Note. Because the script is designed to run forever you might want to run the this baby in a command window under the CScript script host; that way you can stop the script simply by closing said command window. The script will run under WScript, but you’ll then have to use something like Task Manager to terminate the script.

So what happens inside the Do loop? We had a feeling you’d ask that. The very first thing we do inside that loop is execute the following line of code:

Set objLatestProcess = colMonitoredProcesses.NextEvent

What happens here is that the script stops dead in its tracks: it sits there, patiently, and waits for the next event to occur; that is, the script waits for the next member of the __InstanceCreationEvent class to be created (provided, of course, that the item triggering the instance creation happens to be a process). If no new processes are ever created then the script will simply sit there forever and ever.

Of course, sooner or later a new process is bound to be created. When this happens we take the name of the new process, convert it to all lowercase letters (to make sure we don’t run into problems comparing uppercase letters and lowercase letters), and then store the value in a variable named strProcess:

strProcess = LCase(objLatestProcess.TargetInstance.Name)

Once that’s done our next task is to determine whether or not this new process happens to be one of our four target processes, the group of processes we stored in the array arrProcesses. A very easy way to do that is to simply loop through the items in the array and determine whether or not the names of any of those array items happen to match the name of the new process. In fact, that’s what we do here:

For Each strName in arrProcesses
    If strName = strProcess Then
        Wscript.Echo strName & " has started."
    End If
Next

As you can see, this code is about as simple as it gets. All we do is determine whether the first name in the array happens to match the name of the new process (strProcess). If we have a match then we echo the name of the process and the fact that this process has just started. If we don’t have a match we loop around and check the next name in the array. After we’ve checked all four names we then zip back to the beginning of the loop and then wait for the next __InstanceCreationEvent to occur.

That should do it, BC. Like we said, there might be more elegant ways to do this, but, then again, elegance is also a word you rarely see used in the same sentence as “the Scripting Guy who writes this column.” In addition, this monitoring solution should work under any and all conditions, something which isn’t true of the Scripting Son. A week or so ago the batteries in the remote control called it quits, and the Scripting Son was in a panic. “Can’t you go to the store and get some batteries?” he asked. “How am I supposed to watch TV?”

“Can’t you just get up off the davenport and change the channel by hand?” asked the Scripting Dad.

At that point all it took was one look at the Scripting Son’s face for the Scripting Dad to know what had to be done: he went and got new batteries right away. No sense tempting fate, if you know what we mean.