Weekend Scripter: Use PowerShell to Compare Two Snapshots of Running Processes


Summary: Microsoft Scripting Guy Ed Wilson shows how to use Windows PowerShell to compare snapshots of running processes in this step-by-step article.


Microsoft Scripting Guy Ed Wilson here. The road seems to stretch on indefinitely. Parallel lines of concrete, surrounded by tall dancing pine trees and bordered by freshly mown grass, continue ahead as far as the eye can see. Physics tells us that two parallel lines will continue towards infinity without touching. The person who came up with that idea must have been driving on the interstate between Charlotte, North Carolina and Columbia, South Carolina – easily one of the most boring roads in the United States of America. Don’t get me wrong – I like trees. In fact, I love trees; I just prefer trees with more character than the tall, wispy, stick like Southern Pine tree. Palm trees are my current favorite tree. Give me a place with lots of palm trees, and I am at home.

Anyway, I am in my usual passenger seat with my laptop plugged into my power inverter and the Windows PowerShell console open in my 64-bit Windows 7 operating system. We are on our way to speak at another SQL Saturday conference. I decided to play around with the compare object.

The first thing I do is store the process object in a variable. When I am working interactively in the Windows PowerShell console, I quite often use aliases. Just like my real name is not Ed, the real name for the Get-Process cmdlet is Get-Process and not gps. But unlike the command gps, I am not going to tell you what my real name is.

All Windows PowerShell cmdlets return objects. The objects that I am going to store in the $a variable are all instances of the process class, which is found in the System.Diagnostics .NET Framework namespace. Namespaces group related objects in the .NET Framework. At times, the namespaces make sense, such as system.io. At other times, the names are not so obvious. In our process example, System.Diagnostics.Process tells us both the namespace and the class name. Often this is the way I choose to refer to .NET Framework classes because I must know the namespace in which the class resides. Therefore, if I keep saying System.Diagnostics.Process over and over again, eventually I will remember where that class resides, and the next time I need to use it, I will automatically know where the class is found. Of course I can open up MSDN, search for the class name, and eventually find the class reference documentation, assuming I have access to the Internet. However, in the United States, we do not even have ubiquitous cell phone coverage, much less universal freely accessible wireless network access. Therefore, when on the road, I am on my own. Luckily, Windows PowerShell also has the Get-Member cmdlet that will tell me information about the class and its members.

I create the $a variable and store the resulting objects from the Get-Process cmdlet in the variable. This is shown here where I use gps (the alias for Get-Process):

$a = gps

Next, I start a copy of Notepad. I do this by typing the word notepad. Windows PowerShell sees the word notepad, and recognizes that it is not inside a pair of quotation marks, thereby therefore attempting to call a command named notepad. It finds notepad.exe, and launches it. When I have a new process running (notepad.exe), I take another snapshot of all the processes running on the computer by calling the Get-Process cmdlet again. This time, I will store the contents into a new variable called $b. This section of the code is shown here:

 $b = gps

Now compare the two objects. To compare two objects, use the Compare-Object cmdlet. The reference object is the baseline configuration, and the difference object is the object that has changed. My baseline is stored in the $a variable, and the process snapshot I took after starting Notepad is stored in the $b variable. This command and the associated output are shown here:

Compare-Object -ReferenceObject $a -DifferenceObject $b
PS C:\> Compare-Object -ReferenceObject $a -DifferenceObject $b

InputObject                                       SideIndicator
———–                                       ————-
System.Diagnostics.Process (notepad)              =>
System.Diagnostics.Process (sppsvc)               <=

PS C:\>

The output tells me there was a process called sppsvc that was running in the baseline snapshot, but is not running after I launched Notepad. Let’s explore the differences. To examine the notepad process, I need to retrieve it from the collection of processes that are stored in the $b variable. To do this, I use the Where-Object cmdlet. The alias for Where-Object is the question mark. This can be a bit confusing because in the past the question mark has always been used for help, but it kind of makes sense because we are asking the question, “Where is ________?”. Next, we need to retrieve the object that has a name that is equal to notepad. The name property of the process object was discovered via the Get-Member cmdlet. Unfortunately, the output from the Get-Process cmdlet changes the name property to ProcessName. In fact, many of the column headings that are displayed in the Get-Process output are aliases. This is shown here:

PS C:\> $b | Get-Member

   TypeName: System.Diagnostics.Process

Name                       MemberType     Definition
—-                       ———-     ———-
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName
NPM                        AliasProperty  NPM = NonpagedSystemMemorySize
PM                         AliasProperty  PM = PagedMemorySize
VM                         AliasProperty  VM = VirtualMemorySize
WS                         AliasProperty  WS = WorkingSet

Now to hone in on the notepad process, I pipe the process objects that are stored in the $b variable to the Where-Object cmdlet (the ? is an alias for the Where-Object) and filter out the processes with the name property that is equal to notepad. This command and its results are shown here:

 PS C:\> $b | ? { $_.name -eq ‘notepad’ }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
     66       7     1428       4568    62     0.03   5980 notepad

PS C:\>

On the other side of our Compare-Object results was a process named sppsvc. To dig into it, I use the same basic command that I used for returning the process object for the notepad process. The command is shown here:

PS C:\> $a | ? { $_.name -eq ‘sppsvc’ }

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
——-  ——    —–      —– —–   ——     — ———–
    163       8     2680       4996    40            4012 sppsvc

PS C:\>

More information can be returned from a process object than just the seven default properties shown above. To see all the properties and their associated values, I pipe the resulting object to the Format-List cmdlet and choose all the properties (fl * is the alias for this command). The command is shown here:

$a | ? { $_.name -eq ‘sppsvc’ } | fl *

The output is rather long, and to conserve space I have included the screen shot shown in the following image.

Image of output

One property that is a little unique is the Startinfo property. It does not contain a string or an integer; rather, it contains another object. I use the command seen here to view the Startinfo information.

PS C:\> ($a | ? { $_.name -eq ‘sppsvc’ }).Startinfo

Verb                    :
Arguments               :
CreateNoWindow          : False
EnvironmentVariables    : {temp, processor_revision, processor_level, logonserver…}
RedirectStandardInput   : False
RedirectStandardOutput  : False
RedirectStandardError   : False
StandardErrorEncoding   :
StandardOutputEncoding  :
UseShellExecute         : True
Verbs                   : {}
UserName                :
Password                :
Domain                  :
LoadUserProfile         : False
FileName                :
WorkingDirectory        :
ErrorDialog             : False
ErrorDialogParentHandle : 0
WindowStyle             : Normal

PS C:\>


We are pulling into Columbia, South Carolina. I need to hibernate my laptop so that I can prepare to evacuate the motor vehicle. Join me tomorrow for another Weekend Scripter article.

We would love for you follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.


Ed Wilson and Craig Liebendorfer, Scripting Guys


Discussion is closed.

Feedback usabilla icon