Introduction
DiscoProcs does three main things:
- Provides the user with a list of all running processes.
- Allows the user to discover a process' .exe file (path and name).
- Provides some additional information about a process' .exe (info normally found on the Version tab of the file properties dialog).
DiscoProcs stands for Discover Processes -- I admit it, I'm a geek.
Background
I wanted to find out who (company name) owned all these processes running on my machine. The first thing I wanted to know was where these processes were loading from. The next thing I wanted to know was the company/product that was responsible for loading these into the memory every day.
Although I could have listed all the processes in the Task Manager and then fired off a file search for those file names, I wanted a utility which would do all that work for me and show me all the associated info in one place.
While creating the program I discovered many things that I needed the application to do:
Using the code
In the example, I simply make the Get All Procs button (see image above) call the private method GetAllProcs()
.
GetAllProcs(): the easy part
private void GetAllProcs()
{
Process [] localAll = Process.GetProcesses();
m_ProcNames = new string[localAll.Length];
int ProcNameCounter = 0;
foreach (Process proc in localAll)
{
lstProcs.Sorted = true;
lstProcs.Items.Add(proc.ProcessName +
" (procId: " + proc.Id.ToString() +")");
m_ProcNames[ProcNameCounter++] = proc.ProcessName + ".exe";
}
}
That was the easy part.
GetProcInfo(): the convoluted part
The difficult (more convoluted) part is finding the .exe file which is related to the running process. The program uses a brute-force method for finding the file simply by searching all the logical drives on the system.
The code to find the related .exe file runs when the user clicks the Get Proc Info button (see image above). The code looks like the following:
private void GetProcInfo(string [] strProcNames)
{
Hashtable hsDir = new Hashtable(5000);
Hashtable hsFiles = new Hashtable (5000);
string [] Drives = Directory.GetLogicalDrives ();
foreach (string driveName in Drives)
{
if (Directory.Exists(driveName))
{
DiscoFiles.CreateFileList(driveName, strProcNames,
ref hsDir, ref hsFiles, true);
System.Collections.IDictionaryEnumerator FileListEnumerator;
FileListEnumerator = hsFiles.GetEnumerator();
while (FileListEnumerator.MoveNext())
{
lstProcPaths.Items.Add(FileListEnumerator.Key.ToString());
FileVersionInfo myFileVersionInfo =
FileVersionInfo.GetVersionInfo(FileListEnumerator.Key.ToString());
lstProcFileInfo.Items.Add("Company: " +
myFileVersionInfo.CompanyName);
lstProcFileInfo.Items.Add("File Version: " +
myFileVersionInfo.FileVersion );
lstProcFileInfo.Items.Add("Internal name: " +
myFileVersionInfo.InternalName );
lstProcFileInfo.Items.Add("Product name: " +
myFileVersionInfo.ProductName );
}
hsFiles.Clear();
}
}
}
Points of Interest
The not so mysterious case of the freezing interface, starring Nancy Drew
When the code hits the line:
DiscoFiles.CreateFileList(driveName, strProcNames, ref hsDir, ref hsFiles, true);
the application's life seems to be over. This thing is slower than a three-legged dog, running up hill and into the wind.
Well, it's not actually slow, it's just that the method is doing a lot behind the scenes, and we need a good way to update the UI.
If you run the application without anything to handle the work in a thread, or in the background, you'll find that the UI never really gets updated and the main window will look like the following:
A Solution Begging for a Thread
Process: "Please, please Mr. Programmer, give me a thread so I can look like I am fast."
Mr. Programmer: "Okay, but let's check out that Application.DoEvents()
thing first."
Application.DoEvents() -- the lazy programmer's way to thread
The easiest way to allow the UI to update is by adding a call to the Application.DoEvents()
method.
To do that we would just add one line of code in the GetProcInfo()
method. It would look something like the following:
if (Directory.Exists(driveName))
{
Application.DoEvents();
DiscoFiles.CreateFileList(driveName, strProcNames, ref hsDir, ref hsFiles, true);
Hey, DoEvents() doesn't really work
What Does DoEvents() actually do?
DoEvents()
simply tells the application to halt processing each time through a processor intensive loop, to go and check the UI message queue so that if the UI has been updated or if the user has clicked on a control, the application will handle that event in the middle of the intensive algorithm.
Then as soon as the UI event is handled, the process will jump back to the intensive work that your application is doing. It's a pseudo-UI thread thing. In this case, this doesn't work properly, because the call to Application.DoEvents()
would have to be embedded within the CreateFileList()
method, which is really doing the intensive work of searching the drive.
Warning: Using DoEvents()
can allow the user to fire the same code twice. For example, since the UI would become available again, the user has the ability to click the Get Proc Info button again, firing the associated code and causing strange behavior in your code.
How About A Real Thread?
Can we solve this problem using a real thread?
Yes, but only to a certain extent. I'll show you the code and then explain.
Here's the code I've implemented:
private Thread m_myThread;
...
private void btnGetProcInfo_Click(object sender, System.EventArgs e)
{
lstProcFileInfo.Items.Clear();
lstProcPaths.Items.Clear();
m_myThread = new Thread( new ThreadStart(this.GetProcInfoThreadWorker) );
m_myThread.Start();
}
private void GetProcInfoThreadWorker()
{
btnGetProcInfo.Enabled = false;
GetAllProcs();
GetProcInfo(m_ProcNames);
btnGetProcInfo.Enabled = true;
}
private void DiscoProcForm_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
if (m_myThread != null)
{
if (m_myThread.IsAlive)
{
m_myThread.Abort();
}
}
}
Responsive UI
Implementing the code above, makes the UI highly responsive. You can now scroll through the list of process names while the worker thread is doing its thing. You can also minimize the window without it seeming to freeze. The code provides a much better user experience.
Wondering When It Will All End
This works great now. However, the only indication that the user has that the program is doing something is the disabled Get Proc Info button.
Get Procs -- version 2
Please look for my next article, in which, I will address the issue of updating the GetProcs UI. In that article we'll talk about events, delegates, threads and more. See you then.
It could take a while
Since, the code above iterates through every folder on every attached logical drive on your system, it may take a while to find all of the files for all of the processes on your machine.
If you think it's too slow, one thing you can do is change the lines of code which check all drives so that it only checks your C:\ drive.
The following shows you how to do that quickly:
string [] Drives = Directory.GetLogicalDrives ();
string [] Drives = new string[] {"c:\\"};
History
- Version 1 - posted code and article on 11/22/05.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.