Dominik Antal
Dominik Antal

Reputation: 3391

Process Operations (Monitoring)

What I'm trying to accomplish in my program is to knows if certain processes are running (I need to know about all running instances). I want to hold them in a combobox, store as a object, so I can cast them back later. I thought this will be easy but as it turned out, it caused me some headache :P I'm not sure that this is how it should be done, but it's working. However, I'm feel bad about this code solution. I dont know any good programming patterns for this, this is why I ask you fellow coders, to help me out.

The first thing came into my mind is to use a timer to check for processes frequentally and add them, and use the Exited event to remove them from my combobox. So here is my code on Tick event of the timer :

    private void timer_ProcessCheck_Tick(object sender, EventArgs e)
    {
        Process[] tmpArray = Wow_getCurrentlyRunning(); // this returns Process[]
        if (comboBox_processes.Items.Count == 0)
        {
            if (tmpArray.Count() > 0) 
                for (int Index = 0; Index < tmpArray.Count(); Index++)
                    Add(tmpArray[Index]); // adding to combobox
        }
        else
        { 
            if (tmpArray.Count() > comboBox_processes.Items.Count)
            {
                List<Process> result;
        /*Diff compares the two array, and returns to result variable.*/
                if (Diff(tmpArray, comboBox_processes, out result))                 
                    foreach(Process proc in result)
                        Add(proc); // adding to combobox
            }
        }
    }

And my Diff method looks like this, which will put the difference to diff variable.

    public bool Wow_differsFrom(Process[] current, ComboBox local, out List<Process> diff)
    {
        List<int> diffIndex = new List<int>();

        foreach (Process proc in current)
            diffIndex.Add(proc.Id);

        for (byte Índex = 0; Índex < current.Count(); Índex++)
        {
            for (byte Index = 0; Index < local.Items.Count; Index++)
            {
                if (current[Índex].Id == (local.Items[Index] as Process).Id)
                {
                    diffIndex.Remove(current[Índex].Id);
                    break;
                }
            }
        }

        diff = new List<Process>();

        for (int x = 0; x < current.Count(); x++)
            for (int i = 0; i < diffIndex.Count; i++)
                if (current[x].Id == diffIndex[i])
                    diff.Add(current[x]);

        if (diff.Count == 0)
            return false;
        return true;
    }  

Here is the Exited event handler get called upon a process exit

    private void Wow_exitedEvent(object o, EventArgs e)
    {
        RemoveCBItem(comboBox_processes, (o as Process).Id); // this will remove the process from combobox, also threadsafe.
    }

My questions :

  1. How would you do this? Am I approaching this right? I have a feeling, I dont.

  2. Is there any event for application start? Like there is one for exit. Maybe deep in Win32 API?

Upvotes: 0

Views: 302

Answers (1)

Sasha
Sasha

Reputation: 8850

In general the idea is correct I think - if you need to refresh the list of active processes every time. So updating the list using timer is ok. I don't know Win32 API very well, but I think it would be a security issue if anybody could subscribe to process_run and process_retminate wineows events, so it is unlikely to be possible.

But do you really need to update it all the time? Maybe it woule be enough to read process list only when combobox expands? When user will expand it next time, you will reinit items again. I think this approach will cause less problems.

As fo your implementation, I think it is not the most effifient and elegant:

  1. Storing the whole Process object in the combobox item is not good as for me. Better create your class, that will store only those properties you need (Process ID, Process Name)
  2. using current.Count() in a loop is extreemely inefficient - it is extention method that always iterates on IEnumerable on call. So your

    for (byte Índex = 0; Índex < current.Count(); Índex++)

    results in O(N*N) complexity. Fortunately process count will be not too big to affect your application very much, but you should know this fact and do not get used to using this method in a loop. use current.Length instead as it is an array.

  3. Your collection synchronization is too complex and strange. Why not to make a method that receives collection to change and collection to init from and makes first collection to be equal to second using add-remove operations? Of you will have both collections sorted by some property (Process name for example), this can be done very simple and efficient - using binary search. In WPF you can use ObservableCollection as dataSource to use this approach most effectively. In WinForms you probably can also use collections with change notification, but I haven't used them.

You can do it even more simple:

//Somewhere in Form_Load
combobox.DisplayMember = "Name";//name of the property in your MyProcessInfo class
combobox.ValueMember = "Id";//name of the property in your MyProcessInfo class

//In your timer.Tick handler
combobox.DataSource = Wow_getCurrentlyRunning().Select(p=>new MyProcessInfo(p.Id, p.Name)).ToList();

But this approach will always reinit all items if combobox and some blinking is possible.

Upvotes: 1

Related Questions