In this article, we’re going to explore the different ways of working with system processes in WSH. Behind every running program in Windows is a process. Those processes control every end-user function that the computer performs.
Understanding and controlling these processes allows you to do some pretty advanced scripting. Pretty advanced scripting, in turn, requires pretty advanced techniques and for that we turn to WMI.
The Windows Management Interface provides scriptable access to core system components and hardware. It exposes dozens of properties and methods with classes that span nearly every corner of the operating system.
So why might someone want a script to have the ability to control processes?
Well, to be quite simple, if you control the processes, you control the entire operating system. As you can imagine, this can be both extremely useful and potentially dangerous.
On the surface, processes are just programs. Notepad, Calculator, and Microsoft Word are applications that rely on underlying processes. You anti-virus program relies on an underlying process in order to monitor your system's health.
Some processes are more sophisticated and less visible, controlling such things as the Windows desktop, network connectivity, and Windows Update. In fact, many processes run hidden, meaning that they don't display a user interface and they don't appear in the task bar.
The most common scripting tasks are to monitor what tasks are running or to start and stop them. We'll go into detail on each of these as well as other functions such as raising or lowering a processes' priority level.
Our first task is will be to list all currently running processes. This is useful when making a system snapshot or just for curiosity's sake. For this example we'll list each process by Name and Process ID.
Here we're connecting to WMI and querying all objects belonging to the Win32_Process class. This returns a collection of objects representing each of the currently running processes. We are using the Name and ProcessID properties to return the Name and Process ID of each process respectively.
A slight modification to our query allows this code to list specific processes instead. In this above example, I've used a Where clause to limit the objects returned to only those with the name Explorer.exe.
Again, this is useful if you are monitoring a specific process, however, we are kind of assuming that the process exists. Another slight rewrite makes this code a little more effective.
("Select * from Win32_Process Where Name = '" & strProcess & "'")
If colProcess.Count < 0 Then
WScript.Echo "Process is running."
Else
WScript.Echo "Process is not running."
End If
Instead of actually working with the objects, we're simply checking to see if the collection returned any objects. This would be useful if you simply wanted to return a True or False value indicating whether a particular process was active in memory.
A complete list of the properties and methods available through the Win32_Process can be found here.
Now that we've seen how to check whether a process is running, it could be useful to see how to start and stop them accordingly. Again, WMI provides a way for us to do this with very small modifications to our existing code sample.
("Select * from Win32_Process Where Name = '" & strProcess & "'")
For Each objProcess In colProcess
objProcess.Terminate
Next
This code returns all instances of a specified process and then makes use of the Terminate method to end each instance. Starting a process is done slightly differently.
Set objProcess = objWMIService.Get("Win32_Process")
Set objProgram = objProcess.Methods_("Create") _
.InParameters.SpawnInstance_
objProgram.CommandLine = strProcess
Set objProcess = objWMIService.ExecMethod _
("Win32_Process", "Create", objProgram)
Here we resort to the Create method. This method is native to the Win32_Process class and is not provided by its objects as before. This method creates a process instance in memory with the command line provided. This command line should be a full path if this process is not located in the system path.
To this point, everything we've done has been in a snapshot state. By this I mean that we have only been able to work with the processes that are running at the moment our script is executed.
What if we want a script to monitor processes and notify us whenever one starts or stops? To do this, we can use the Win32_Process class with a bit of event-based programming.
Event scripting is slightly beyond the scope of this article. Understand that WMI is able to monitor certain events. In this example we are monitoring the __InstanceCreationEvent which tells WMI whenever a new object is created in memory. We then use a notification query to check whether that particular event was our process starting.
Notice how we've used an infinite loop to ensure that our script doesn't stop running. Thus it will continue to monitor each new event until Internet Explorer is started.
Waiting for a process to end is exactly the same except that we will be watching the __InstanceDeletionEvent to know when a process is unloaded from memory instead.
Again, the nature of how this code works is a bit beyond the scope of this article. Just understand that this is the basic query structure you will need to use. You can modify this query slightly to achieve other effects as well so don't be afraid to play with it. For more information, you can check out my articles on event scripting with WMI.
The final set of code samples I have for you is designed for special needs when working with processes that you just may develop a need to script. We'll begin by modifying a specific process and then we'll look at some cool things we can do with our own.
("Select * from Win32_Process Where Name = '" & strProcess & "'")
For Each objProcess In colProcesses
result = objProcess.SetPriority(ABOVE_NORMAL)
Next
The setPriority method allows you to control the priority of a running process. It accepts predefined constant values to indicate the priority level and issues a return code from the table below.
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.PriorityClass = ABOVE_NORMAL
Set objProcess = GetObject("winmgmts:rootcimv2:Win32_Process")
objProcess.Create strProcess, Null, objConfig
In the same way, it might also be useful to start a process at a specific priority level. This code does just that for you. It's a slightly modified combination of two examples you've already seen. The PriorityClass property accepts the same values as the setPriority method that we just examined.
But settings a process' priority isn't all there is to do, is it? It might also be nice to control how the process starts and runs. After all, most processes run hidden, don't they?
Set objStartup = objWMIService.Get("Win32_ProcessStartup")
Set objConfig = objStartup.SpawnInstance_
objConfig.ShowWindow = HIDDEN_WINDOW
Set objProcess = GetObject("winmgmts:rootcimv2:Win32_Process")
objProcess.Create strProcess, Null, objConfig
This time we rely on the ShowWindow property of the Win32_ProcessStartup class to set our process to run hidden. This additional class allows us to control how processes start. There are many properties and methods available with this class to control how a process starts and operates. For more information, visit the link below.
The final example I would like to provide for you is one that I sometimes find very useful. It gives you the ability to prevent a process from running. Essentially, it monitors for a process to start and then ends it.
Set colProcess = objWMIService.ExecNotificationQuery _
("Select * from __InstanceCreationEvent" _
& " WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'")
Do While True
Set objLatestProcess = colProcess.NextEvent
If objLatestProcess.TargetInstance.Name = strProcess Then
objLatestProcess.TargetInstance.Terminate
End If
Loop
This code basically combines our examples for monitoring a process start-up and ending a process. Again, this code can be further modified to suit your needs. This example demonstrates how you can combine the techniques you've seen to produce a usable script.
Take the time to explore the MSDN documentation for the Win32_Process and Win32_ProcessStartup classes. They provide many other properties and methods that I was unable to include for lack of space. By reading the documentation and getting to know these WMI classes, you can add a lot of power to your scripts and more weapons to your scripting arsenal. Until next time, keep coding!