Hey, Scripting Guy! How can I get a list of all the users who have a process open on a computer?
— RE
Hey, RE. You know, over the past 5 years the number of people who watch the news on the major networks (ABC, NBC, and CBS) has dropped precipitously; the number of people who read the newspaper each day has fallen even more dramatically. And yet, at the same time, the number of people who visit the Script Center each day has skyrocketed. That can mean only one thing: when people want news and information, they turn to the Script Center first.
And, really, who can blame them? While we’re sure that the major networks and newspapers mean well, they simply don’t cover important stories of the day. For example, suppose you want to know the status of the Dr. Scripto bobblehead dolls. Are you going to find that information in the New York Times? No. Is CBS going to interrupt its regularly-scheduled programming in order to bring you news about the bobbleheads? Yeah, right. Just where can you turn for late-breaking bobblehead bulletins?
As if you had to ask.
Speaking of which, we just happen to have a late-breaking bulletin for you: the bobbleheads are done and ready to be shipped.
In fact, the Scripting Guys have actually received a box of bobbleheads, and they’re pretty cool. For one thing, they’re solid and substantial; they aren’t made of cheap, flimsy plastic (like Scripting Guy Dean Tsaltas is). And, their heads actually bobble (just like Scripting Guy Peter Costantini’s does). Is there anything in the entire world better than a Dr. Scripto bobblehead doll? No, there isn’t.
For those of you who have been waiting on pins and needles for the past 6 weeks or so, the Scripting Guys have filled out all the paperwork and sent the appropriate names and addresses to the bobblehead maker; we expect that they’ll begin air mailing the bobbleheads within the next few days. If you were a lucky winner during the 2007 Winter Scripting Games, well, the waiting is almost over.
And what if you weren’t a lucky winner during the 2007 Winter Scripting Games; does that mean you’ll never be able to get a Dr. Scripto bobblehead of your own? Yes, that’s exactly what it means. Too bad; life’s tough, isn’t it?
Wait, wait, don’t cry; we’re sorry. OK, tell you what: if you promise to enter the 2008 Winter Scripting Games next February, and if you promise to read all the way down to the end of today’s column we’ll give you another chance to (maybe) win a Dr. Scripto bobblehead doll. But whatever you do, don’t try to cheat by simply scrolling down the end of today’s column; that won’t work.
Oh; really? OK. Never mind; the Scripting Editor has informed us that simply scrolling down to the end of today’s column will work. But don’t do that; after all, if you do that then you’ll never know how to get a list of all the users who have a process open on a computer:
strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process") Set objDictionary = CreateObject("Scripting.Dictionary") For Each objProcess in colProcessList objProcess.GetOwner strNameOfUser, strUserDomain strOwner = strUserDomain & "\" & strNameOfUser If Not objDictionary.Exists(strOwner) Then objDictionary.Add strOwner, strOwner End If Next For Each strKey in objDictionary.Keys Wscript.Echo strKey Next
So how does this work? Well, it – wait a second: are you really interested in the script or are you just pretending to be interested just in case that helps you get a bobblehead doll? Oh, OK. In that case, the script starts out by connecting to the WMI service on the local computer (although we can also run this same script against a remote machine). After making the connection we then use this line of code to return a collection of all the processes running on the computer in question:
Set colProcessList = objWMIService.ExecQuery("Select * From Win32_Process")
Once we have our collection safely tucked away in the variable colProcessList we then create an instance of the Scripting.Dictionary object:
Set objDictionary = CreateObject("Scripting.Dictionary")
Why do we create an instance of the Scripting.Dictionary object? We’ll explain that in just a second.
Before we do that, however, we need to set up a For Each loop to loop through all the processes in our collection. Inside that loop we first execute these two lines of code:
objProcess.GetOwner strNameOfUser, strUserDomain strOwner = strUserDomain & "\" & strNameOfUser
What are we doing here? Well, for some reason the Win32_Process class doesn’t include a property that tells you the owner of a process (the owner of a process being the same thing as the account under which the process is running). Instead, for each process in the collection we need to call the GetOwner method in order to determine the process owner. As you can see, when we call GetOwner we supply two parameters; in this case, strNameOfUser and strUserDomain. Both of these are “out” parameters. An out parameter is simply a variable that we supply to a method; in return, that method populates those two variables with the appropriate values. In this script that means that GetOwner will assign the name of the process owner to strNameOfUser, then assign that user’s domain to strUserDomain. And yes, those names are completely arbitrary; we can name the out parameters anything we want. Would you rather have out parameters named x and y? Okey-doke:
objProcess.GetOwner x, y
The important thing here is not the names but the order: the variable specified first will get the owner name and the variable specified second will get the owner domain.
Once we have that information we then use the second line of code to combine the name and domain in this format: Domain\Username (e.g., FABRIKAM\kenmyer).
That was pretty easy, wasn’t it?
Note. No, agreeing with the Scripting Guys won’t help you get a bobblehead. But it won’t hurt, either. |
That brings us to this block of code:
If Not objDictionary.Exists(strOwner) Then objDictionary.Add strOwner, strOwner End If
This is where the Dictionary object comes into play. Each process running on a computer has an owner; if you have 50 processes running on your machine then there will be 50 process owners. However, it’s unlikely that you’ll have 50 unique owners; instead there are bound to be duplicates (for example, a large percentage of your processes will be running under the NT AUTHORITY\SYSTEM account). We don’t really need to – or want to – see all those duplicate names. But how can we echo back only unique owner names?
One way is to use the Dictionary object. As you can see, in our first line of code we use the Exists method to determine whether the owner of the first process (e.g., FABRIKAM\kenmyer) can be found in the Dictionary. Let’s say that this process owner isn’t found in the Dictionary. (Needless to say, the owner of the first process won’t appear in the Dictionary, simply because the Dictionary will be empty the first time through the loop.) In that case we then call the Add method to add the process owner to the Dictionary, using the variable strOwner to specify both the Dictionary Key and Item:
objDictionary.Add strOwner, strOwner
Note. What’s that? You say you don’t understand how the Dictionary object works? Tsk, tsk, tsk: you haven’t been reading Sesame Script, have you? |
We then loop around and repeat the process with, well, with the next process in the collection. Let’s say that process No. 2 is also owned by FABRIKAM\kenmyer. This time when we call the Exists method the process owner will be found in the Dictionary. What happens then? Nothing; we simply loop around and start all over again with the third process in the collection.
When we’ve finished retrieving the owner of each process we then echo out the value of the Dictionary keys. By amazing coincidence, those keys will represent the unique set of process owners:
NT AUTHORITY\SYSTEM NT AUTHORITY\NETWORK SERVICE NT AUTHORITY\LOCAL SERVICE FABRIKAM\kenmyer
And there you have it.
Note: If you run this script on Windows Vista, you’ll see different results when you Run As Administrator rather than simply run the script as an Administrator. Make sense? No, it doesn’t, does it? But what it means is that if you’re logged on as an administrator and you open a command prompt and run this script, you won’t get the full list of process owners. In order to get the full list, you have to right-click on the command prompt shortcut and select Run As Administrator. When you run the script from that window, you’ll see the full list of process owners. |
Now, what about those bobbleheads? Well, as it turns out, the Scripting Guys will be attending this year’s TechEd conference in Orlando, FL (June 4-8). As part of the festivities the Scripting Guys will spend a good part of their time sitting at the TechNet Magazine booth in the Exhibition Hall, handing out a fun little giveaway and giving away bobbleheads. (How many bobbleheads? Good question; however, we have to wait and see what the budget looks like first before we can answer it.) If you’d like another shot at a bobblehead doll then all you have to do is sign up for TechEd, swing by the TechNet Magazine booth, say hi to the Scripting Guys, and enter the drawing.
Sure, that sounds like a lot to do. But what do you expect when the grand prize is a Dr. Scripto bobblehead doll?
Can’t make it to Orlando this year? Well, with any luck we’ll also be attending TechEd Europe in November. Can’t make it to TechEd Europe, either? Sheesh; what do you want us to do, just hand out bobbleheads to everyone who asks for one? Oh. Well, sorry: we definitely don’t have the budget for that. But we’ll make one final deal with you: after the smoke clears from TechEd we’ll see what we can do about giving everyone one last shot at a bobblehead doll. Because we agree with you: living without a Dr. Scripto bobblehead doll really isn’t living, is it?
Determining Process Ownership Using Windows PowerShell
The Windows PowerShell solution to this question is made much easier by the presence of the Group-Object cmdlet, which groups objects based on the value of a selected property. The default display of a Group-Object command lists the property value, the names of objects in each group, and the number of objects in each group. As a result, the Scripting Guy’s nifty scripting dictionary technique is not needed here.
The following Windows PowerShell command answers the user’s question:
get-wmiobject win32_process | group-object -property {$_.getowner().domain}, {$_.getowner().user}
Here’s the output. It’s not exactly like the output of the VB script, but we also haven’t put any effort into it, and we get a bit more information.
Count Name Group ----- ---- ----- 1 {System Idle Process} 27 NT AUTHORITY, SYSTEM {System, smss.exe, csrss.exe, winlogon.exe...} 5 NT AUTHORITY, NETWORK ... {svchost.exe, svchost.exe, AdtAgent.exe...} 4 NT AUTHORITY, LOCAL SE... {svchost.exe, wdfmgr.exe, alg.exe...} 3 SERVER01, Administrator {init, inetd, cron} 31 DOMAIN01, user01 {explorer.exe, smax4pnp.exe...}
To eliminate the truncated Name values, you can send it to the Format-Table cmdlet and use its -Wrap parameter.
get-wmiobject win32_process |
group-object -property {$_.getowner().domain}, {$_.getowner().user} |
format-table -wrap
Understanding the Command
This command uses the Get-WmiObject cmdlet with the Win32_process class. This returns a Win32_Process object for each process on the computer.
get-wmiobject win32_process
We use a pipeline operator (|) to send the results to the Group-Object cmdlet. Group-Object has a -Property parameter that specifies the properties to group by. If you give it more than one property, it groups first by the first property value and then by each subsequent property value.
get-wmiobject win32_process | group-object -property
In this case, we follow the model of our hero, the Scripting Guy [bowing respectfully]. We need to give Group-Object a property, but first, we use the GetOwner method to get the object that has the Domain and User properties.
For each property, we use the $_ symbol to represent the current object, that is, each Win32_process object that Get-WmiObject sends through the pipeline to Group-Object. The $_ is followed by a dot (.), the name of the method (GetOwner), and a set of parentheses to indicate that it’s a method, not a property.
$_.getowner()
Now, we’ll get the Domain and User properties of the object that the GetOwner method returns. Just follow the method with another dot (.) and the property name.
$_.getowner().domain $_.getowner().user
Because we’ve written an expression to get each property, we’ll enclose each expression in curly braces {}.
{$_.getowner().domain} {$_.getowner().user}
We’re going to use the -Property parameter of Group-Object to specify the two properties to group by. When you specify more than one value for a Windows PowerShell parameter, you separate the values with a comma. And, be sure to specify them in the right grouping order. First domain, then user.
group-object -property {$_.getowner().domain}, {$_.getowner().user}
Now, we’ll put it all together.
get-wmiobject win32_process | group-object -property $_.getowner().domain}, {$_.getowner().user}
Process Objects v. Process Objects
For Windows PowerShell users, the interesting part of this task — the part that the Scripting Guy did for us — was to realize the difference between the process object that the Get-Process cmdlet returns and the process object that WMI returns.
Let’s use the Get-Member cmdlet to look at them. Get-Member gets the .NET type of the object and all of its properties and methods.
First, we send the results of a Get-Process command to Get-Member.
get-process | get-member
It shows that you have a System.Diagnostics.Process object and it lists all of its gazillion properties and methods. I won’t list them here, but you can see it on your machine or on MSDN.
Now, we’ll send the results of a Get-WmiObject Win32_process command to Get-Member.
get-wmiobject win32_process | get-member
Surprise. We have a System.Management.ManagementObject#root\cimv2\Win32_Process object, which is essentially a WMI process object. Again, a bunch of very useful properties and methods (per MSDN).
These objects have some common properties, like WorkingSet (Get-Process) and WorkingSetSize (WMI), and SessionID (both). They have some common methods, like Terminate (WMI) and Kill (Get-Process), but they have lots of disjoint properties and methods. And, if you use Windows PowerShell, this is a great benefit, because you have them both.
So, the next time you need something from an object, check both the PowerShell and WMI versions of the object.
Oh, and do whatever you can to get a Dr. Scripto bobblehead. I have one on my desk (with a Windows PowerShell logo) and my life will never be the same.
0 comments