Click here to Skip to main content
15,902,189 members
Articles / Programming Languages / C#
Article

DiscoProcs

Rate me:
Please Sign up or sign in to vote.
3.20/5 (6 votes)
23 Nov 20055 min read 27.4K   567   18   2
Determine what processes are running and who owns them.

Get Procs Ver. 1 -- Main View

Introduction

DiscoProcs does three main things:

  1. Provides the user with a list of all running processes.
  2. Allows the user to discover a process' .exe file (path and name).
  3. 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:

  • Get all process names (System.Diagnostics.Process).
  • Find all files that matched a specific pattern -- this program implements my DiscoUtils library (method: CreateFileList) which I wrote about in a previous article (search for: Find All Files or DiscoUtils on CodeProejct).
  • Get file information (System.Diagnostics.FileVersionInfo).
  • Find all logical drives on the system (Directory.GetLogicalDrives -- so I could search all the drives for a process' exe file).
  • Update the UI with the information in a timely manner -- so the UI wouldn't freeze (System.Threading.Thread & ThreadStart methods).

    Note: This opened up a whole can of worms related to events and delegates, which I'll cover in the Version 2 of the DiscoProcs utility.

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

C#
private void GetAllProcs()
{
    // Call the static member GetProcesses which returns
    // an array of processes
    Process []  localAll = Process.GetProcesses();
    // new up the member string array
    // to hold all of proc names for later use
    m_ProcNames = new string[localAll.Length];
        
    // 1) iterate through the processes 
    // 2) add the proc names and ids to the listbox
    int ProcNameCounter = 0;
    // need this counter so I can change
    // the index of the ProcNames array
    foreach (Process proc in localAll)
    {
        //lstProcs is the listbox 
        //at the top of the form (see image above)
        lstProcs.Sorted = true;
        lstProcs.Items.Add(proc.ProcessName + 
                 " (procId: " + proc.Id.ToString() +")");
        // lazy-man code -- set the value of the current string, 
        // then increment ProcNameCounter using postfix incremeter  
        m_ProcNames[ProcNameCounter++] = proc.ProcessName + ".exe";
        // m_ProcNames is used later in call to CreateFileList() method
    }
}

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:

C#
private void GetProcInfo(string [] strProcNames)
{
    // prepare a couple of hashtables to hold dir and file names
    Hashtable hsDir = new Hashtable(5000);
    Hashtable hsFiles = new Hashtable (5000);
    // load all of the logical drives
    string [] Drives = Directory.GetLogicalDrives ();
    // see It Could Take A While section in article
    
    foreach (string driveName in Drives)
    {
        // this checks to see if there is a disk in the drive
        if (Directory.Exists(driveName))
        {
            // calls the method to find all files
            // that match the name of the processes
            // See codeproject article "find all files" for more details
            DiscoFiles.CreateFileList(driveName, strProcNames, 
                                ref hsDir, ref hsFiles, true);

            //set up an enumerator to use to iterate 
            //through the files returned from CreateFileList
            System.Collections.IDictionaryEnumerator FileListEnumerator;
            FileListEnumerator = hsFiles.GetEnumerator();

            //iterate through the list of files
            while (FileListEnumerator.MoveNext())
            {
                // add the path and name to the middle list box
                lstProcPaths.Items.Add(FileListEnumerator.Key.ToString());
                // create a fileversioninfo object
                // so we can retrieve additional info
                // from the file, which will help us understand
                // more about the file and running process
                FileVersionInfo myFileVersionInfo = 
                  FileVersionInfo.GetVersionInfo(FileListEnumerator.Key.ToString());

                //add additional pieces of information
                // (FileVersionInfo) to the bottom list box
                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:

C#
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:

Get Procs -- not responding

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:

C#
if (Directory.Exists(driveName))
{
    // calls the method to find all files that match the name of the processes
    // See codeproject article "find all files" for more details
    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.

Get Procs -- with thread

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:

C#
//First, I add a new member var. (a System.Threading.Thread) to the class
private Thread m_myThread;
// It will become apparent, later why we add this as a member var.
...

// here's the method that runs when the user clicks the Get Proc Info button
private void btnGetProcInfo_Click(object sender, System.EventArgs e)
{
    // empty the list boxes so we can add only the latest info to them
    lstProcFileInfo.Items.Clear();
    lstProcPaths.Items.Clear();


    // Create a new thread which is attached
    // to the local worker method called GetProcInfoThreadWorker
    m_myThread = new Thread( new ThreadStart(this.GetProcInfoThreadWorker) );
    // fire the thread (start)
    m_myThread.Start();
    // at this point GetProcInfoThreadWorker() is invoked on its own thread
}

// all of this code now runs on it's own thread
// -- meanwhile the UI is running on its own thread
// That means the UI can react to user events and refresh itself
private void GetProcInfoThreadWorker()
{
    // disable the button, so user can't call
    // the code again until we are done
    btnGetProcInfo.Enabled = false;
    // insure that GetAllProcs is called
    // so array is filled with process names
    GetAllProcs();
    // call the method that is processor intensive
    GetProcInfo(m_ProcNames);
    // enable the button again, since we are done
    btnGetProcInfo.Enabled = true;
}

// One more problem we have to solve,
// is if the user clicks the Close (X) button
// to close the application while the worker thread is running.
// If the user does this the Main Form
// will close and disappear, however, the worker thread
// will continue to run until it has completed.
// This method handles that situation
private void DiscoProcForm_Closing(object sender,
             System.ComponentModel.CancelEventArgs e)
{
    // here's where I fulfill the promise of using the member m_myThread
    // insure that the thread has been new'd up
    if (m_myThread != null)
    {
        // is the thread still alive
        if (m_myThread.IsAlive)
        {
            // abort the thread
            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:

C#
// comment or delete the following line in GetProcInfo() method 
string [] Drives = Directory.GetLogicalDrives ();
// add the following line
string [] Drives = new string[] {"c:\\"};

History

  • Version 1 - posted code and article on 11/22/05.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralExecutable name and version Pin
Richard Deeming30-Nov-05 1:27
mveRichard Deeming30-Nov-05 1:27 
GeneralSimplification to GetProcInfo() Pin
SysSpider23-Nov-05 8:58
SysSpider23-Nov-05 8:58 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.