Summary: Learn how to use Windows PowerShell and WMI to terminate multiple processes.
Hey, Scripting Guy! I am wondering about how to use WMI to work with multiple instances of a class. For example, if I have 50 processes running, and I want to kill all of them at the same time, I can easily do this using the Get-Process and Stop-Process cmdlets. What if I want to use WMI instead? What do I need to do?
—CT
Hello CT,
Microsoft Scripting Guy Ed Wilson here. Last night’s PowerShell user group meeting in Corpus Christi, Texas was a lot of fun. This was their first meeting, and Marc Adam Carter, the chapter president did an awesome job putting things together! It is a lot of work to start a PowerShell users group, and I really appreciate his enthusiasm. The Scripting Wife and I really enjoyed speaking and hanging out with people who share our passion for Windows PowerShell. If you ask her (the Scripting Wife that is), she will tell you my favorite two things are Windows PowerShell and talking to IT pros. When I combine the two activities, I am a little hard to control. Once again, the Highway to PowerShell rocked the walls and heralded my presentation.
Suppose I have fifty copies of Notepad running—that’s right, fifty. Don’t ask what I am doing with fifty copies of Notepad running. It just seems to happen at times, particularly if I use the range operator, a ForEach-Object statement, and call Notepad. Now, if I use WMI and the Win32_Process class, I can filter out all processes except for Notepad and store the results in a variable. I can then use the count property to see how many instances of Notepad are running. Now, if I attempt to call the Terminate method, it will fail. But, my first indication that things are somewhat awry is that tab expansion for the Terminate method does not work. The reason for the failure is that I am dealing with an array instead of a single instance of a process. The commands I have used are shown here:
1..50 | % {notepad}
$a = Get-WmiObject win32_process -Filter "name = 'notepad.exe'"
$a.Count
$a.terminate()
The commands and the associated output are shown here.
If I want to terminate all 50 instances of Notepad, I can use the instances that are stored in the $a variable, but I will need to walk through the array and call the Terminate method once for each instance of the process. To do this, I find it easiest to use the pipeline, and the Foreach-Object cmdlet to permit me to work with each instance. The Foreach-Object cmdlet has the % alias; therefore, it is really easy to use at the command line inside the Windows PowerShell console. Because I am going to terminate 50 processes, I do not want to clutter my console with 50 return codes, so I pipe the output to the Out-Null cmdlet (to discard the return values). After I have terminated the 50 processes, I use the Get-WmiObject cmdlet to query once again for instances of notepad.exe. No error is returned if no instances of the process appear. These two commands are shown here:
$a | % { ([wmi]$_.__RELPATH).terminate() | out-null }
Get-WmiObject win32_process -Filter "name = 'notepad.exe'"
The commands and the associated output are shown in the following figure.
I can use the Invoke-WmiMethod cmdlet to terminate all 50 instances of Notepad. First, I create 50 instances of Notepad. Next, I use the Get-WmiObject cmdlet to retrieve all the instances of Notepad. Then I pipe the objects to the Foreach-Object cmdlet. In the Invoke-WMIMethod cmdlet, I use the inputobject parameter to receive the object upon which to work. The cmdlet is smart enough to retrieve the relative path upon which to work. The commands are shown here:
1..50 | % {notepad}
$a = Get-WmiObject win32_process -Filter "name = 'notepad.exe'"
$a | % {Invoke-WmiMethod -Name terminate -InputObject $_ | out-null}
The commands and associated output are shown in the following figure.
Of course, it is possible to simplify working with the Terminate method. If I have to perform a WMI query to return all instances of the notepad.exe process, why store the results into a variable only to pipe the results and call a method? I can cut out the intermediary, and call the method directly. In this example, I first create 50 instances of Notepad, and then I use the Get-WmiObject cmdlet to find all instances of the notepad.exe process. I then pipe the results to the ForEach-Object cmdlet (using the % alias) and call the Terminate method from the piped object. I then write all 50 return codes back to the $null variable. The last Get-WmiObject command is used to show that no instances of Notepad are left running. The commands are shown here:
1..50 | % {notepad}
$null = Get-WmiObject win32_process -Filter "name = 'notepad.exe'" | % {$_.Terminate() }
Get-WmiObject win32_process -Filter "name = 'notepad.exe'"
The commands and associated output are shown in the following figure.
I can use exactly the same technique to work with multiple processes. I modify the command that creates 50 instances of Notepad, so that it also creates 50 instances of the calc.exe process. Next, I modify the WMI filter parameter so that it returns all instances of both Notepad and Calculator. The remainder of the command is exactly the same: I pipe the resulting WMI objects to the ForEach-Object cmdlet, and I call the Terminate method of each object as it crosses the pipeline. I store the return codes in the $null variable and therefore discard them and avoid cluttering the Windows PowerShell console. The commands are shown here (the second command is a single logical command; it is broken at the pipeline character for better display on this blog):
1..50 | % {notepad;calc}
$null = Get-WmiObject win32_process -Filter "name = 'notepad.exe' OR name = 'calc.exe'" |
% { $_.Terminate() }
There is no output from the above commands, and therefore no need for another screen shot.
CT, that is all there is to using WMI instance methods when working with multiple instances of the WMI class. WMI Method Week will continue tomorrow when I will continue talking about working with WMI methods.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy