Introduction
This little utility will allow you to view all the processes which start and stop on your machine -- even when you've locked the screen. It's easy to use and the code reveals a couple of interesting tricks.
Quick Fix - V2*
Soon after release, I noticed that I forgot to provide a scrollbar on the Multi-line TextBox
. It's very simple to change, but a bit annoying. Now, you can leave the application run for a long time and scroll through the long list of items.
Background
I wanted an easy way to track programs (processes) which start and stop on my machine even when I locked it and left for lunch or a meeting. I found this little trick which leverages the WMI (Windows Management Instrumentation) which I often run in a copy of LinqPad (see http://Linqpad.net^ for more). Sometimes, you just want to know what your computer is doing and what processes run which are started by the OS.
Not Perfect, But Extremely Usable
There are a few things to know before I show you the code.
I created this app very quickly to solve a specific problem. That means I've implemented the functionality in a "get 'er done" kind of way. What things might you notice?
- TextBox output: I used a simple
textbox
for the output. It's good enough. I Append()
to a StringBuilder
and output the string
to the TextBox
each time a new WMI event fires. If you don't like that, you can always download the code and change it.
- Delimited output: I've quickly tab and pipe delimited the text so you can easily import the data into Excel if you like. It's not the most beautiful, but again it works.
- SaveFile: The
SaveFileDialog
is the generic one and I don't set any wildcard or extension or anything. You can save the data in the output very quickly (I use System.IO.File.AppendAllText
) and then use the data however you want. It's simple and quick.
- OOP Is Alive: One thing I did not skimp on, however, is separating out the functionality so you can easily grab the class and use it in your own application. You'll find the core code in the EventWatcher.cs. Since I am updating the
TextBox
control on the MainForm
, I do require a reference to that form and I pass it in on the constructor -- so it is somewhat tied to that form. But all of the code which does the work of watching processes on your Windows machine will be separated enough that it'll be easy to use the class in your own project if you like.
Now, let's get to it. I'll show you how the little bit of code works.
Using the Code
Main Point of this Utility
The main point of this application is to allow it to run for a long time (1 or 2 days) so you can see the numerous processes which are started by the OS itself -- or other installed software. This can help you find processes you never knew were running before.
Let's take a look at the main worker class (EventWatcher
) in the project because it reveals the easy way to watch processes on your computer.
The C# class called ManagementEventWatcher
(you'll need a reference to System.Management.dll) provides a nice wrapper to the WMI interface. The one tricky thing about it, however, is that the methods you call start listeners to systems events which are basically threads you'll have to manage.
In the EventWatcher
's constructor, the first thing I do is register (save off) the form that I want to update with the data (string
s) I get back from the ManagementEventWatcher
, which will represent the information about the process which has started or stopped. The constructor looks like the following:
public EventWatcher(MainForm targetForm)
{
outputForm = targetForm;
WatchForProcessStart();
WatchForProcessEnd();
}
After I set up the form that will be updated, I make two calls to methods I've written:
WatchForProcessStart()
WatchForProcessEnd()
Let's look closer at the first one. The second one is exactly the same except for a slight change in the WMI query.
public void WatchForProcessStart()
{
string queryString =
"SELECT TargetInstance" +
" FROM __InstanceCreationEvent " +
"WITHIN .025 " +
" WHERE TargetInstance ISA 'Win32_Process' "
+ " AND TargetInstance.Name like '%'";
string scope = @"\\.\root\CIMV2";
startProcWatcher = new ManagementEventWatcher(scope, queryString);
startProcWatcher.EventArrived += ProcessStarted;
startProcWatcher.Start();
}
To create a new ManagementEventWatcher
, you need to supply a scope and a queryString
to its constructor.
You can see that the first two statements set up both of those items.
WMI QueryString
The interesting parts of the queryString
are the system object we are selecting from (__InstanceCreationEvent
) and the TargetInstance
system object which we check to insure is a Win32_Process
and then check its Name
property. Also, from everything I can tell, the Win32_process
really "means any running process on your system" and is somewhat of a misnomer and a throwback to the past. This query will result in showing you the x64 processes running too. If you find out differently, please comment back.
WMI Scope
I basically copied the value that the scope
object expects for the local computer. There is a way to pass in a ComputerName
to replace the dot so that you can run this query against another computer, but that will also require some security set up and is beyond the scope of this tip.
Using the ManagementEventWatcher
Once you have those items set up, you can instantiate your ManagementEventWatcher
object and initialize it for your use.
It's as simple as setting the delegate (callback) method that will get called when the system registers the process start (or stop). You set up the event method by simply pointing it at the method you want to run when the event fires.
The set up looks like the following:
startProcWatcher.EventArrived += ProcessStarted;
After setting that up, we simply call the Start()
method and the system will begin notifying us when a process starts on the current computer.
The code that runs when the event fires looks like:
private void ProcessStarted(object sender, EventArrivedEventArgs e)
{
ManagementBaseObject targetInstance =
(ManagementBaseObject)e.NewEvent.Properties["TargetInstance"].Value;
string processName = targetInstance.Properties["Name"].Value.ToString();
string exePath = targetInstance.Properties["ExecutablePath"].Value.ToString();
string action = "BEGAN";
outputForm.displayDelegate(String.Format("{0}|\t{1}|\t{2}|\t{3}",
DateTime.Now, action, processName, exePath));
}
As you can see, we just pull some data out, format a string
for our output and then call our displayDelegate()
method, which is simply a way to call the method in our MainForm
which will show the text in our TextBox
.
Small Details for Saving File
I wanted to let you start this up and run it and then save off all the data so I made it so that if you righ-click on the TextBox
, then you can choose the Save To File menu item so you can save it to a simple text file which you can open with any editor. You can use Excel and do more analysis or just sorting if you like.
Points of Interest
UI: Enable/Disable Buttons and Background color
One last thing. So you can know whether or not the application is currently polling, I set the background to a light yellow and when it is stopped I set it back to white. I also enable disable the buttons so they can only be used at the correct time and to provide UI feedback about what the app is doing.
Extra
Just as I was doing my final tests, before publishing this tip, I noticed that if the query was running and you close the application, without first stopping, then the app would throw an exception. I put a fix in for this which looks like the following -- basically stops the listeners if the user closes the application while they are running.
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (ew != null)
{
ew.StopAllWatchers();
ew = null;
}
}
History
- Second release 2015-12-21 (later in the day): Noticed that the Multi-line textbox did not have a vertical scroll bar, so I added it so you can scroll through a long list of items after allowing the application to run for a long time
- First release: 2015-12-21