0

我在我的程序中想要完成的是知道某些进程是否正在运行(我需要知道所有正在运行的实例)。我想把它们放在一个组合框中,作为一个对象存储,这样我以后可以把它们放回去。我认为这很容易,但事实证明,这让我有些头疼:P 我不确定这是否应该这样做,但它正在工作。但是,我对这个代码解决方案感觉很糟糕。我不知道任何好的编程模式,这就是为什么我请你们编码人员帮助我。

我想到的第一件事是使用计时器经常检查进程并添加它们,并使用 Exited 事件将它们从我的组合框中删除。所以这是我关于计时器 Tick 事件的代码:

    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
            }
        }
    }

我的 Diff 方法看起来像这样,它将差异放入 diff 变量。

    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;
    }  

这是在进程退出时调用的 Exited 事件处理程序

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

我的问题:

  1. 你会怎么做?我接近这个权利吗?我有感觉,我没有。

  2. 申请开始有什么活动吗?就像有一个出口。也许深入 Win32 API?

4

1 回答 1

1

总的来说,我认为这个想法是正确的 - 如果您每次都需要刷新活动进程列表。所以使用计时器更新列表是可以的。我不太了解 Win32 API,但我认为如果有人可以订阅 process_run 和 process_retminate wineows 事件,这将是一个安全问题,因此不太可能。

但是你真的需要一直更新吗?也许只有在组合框扩展时才读取进程列表就足够了?当用户下次展开它时,您将再次重新初始化项目。我认为这种方法会导致更少的问题。

至于您的实现,我认为它不是最有效和最优雅的:

  1. 将整个 Process 对象存储在组合框项中对我来说并不好。更好地创建您的类,它将仅存储您需要的那些属性(进程 ID、进程名称)
  2. 在循环中使用 current.Count() 效率极低 - 它是一种扩展方法,总是在调用时迭代 IEnumerable。所以你的

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

    导致 O(N*N) 复杂度。幸运的是,进程数不会太大而不会对您的应用程序产生太大影响,但是您应该知道这一事实,并且不要习惯于循环使用此方法。使用 current.Length 代替,因为它是一个数组。

  3. 您的收藏同步太复杂和奇怪。为什么不创建一个方法来接收要更改的集合和要从中初始化的集合,并使用添加-删除操作使第一个集合等于第二个集合?你们中的两个集合都按某些属性(例如进程名称)排序,这可以非常简单有效地完成 - 使用二分搜索。在 WPF 中,您可以使用 ObservableCollection 作为数据源来最有效地使用这种方法。在 WinForms 中,您可能还可以使用带有更改通知的集合,但我没有使用它们。

你可以做的更简单:

//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();

但是如果组合框和一些闪烁是可能的,这种方法将始终重新初始化所有项目。

于 2012-05-10T14:55:04.357 回答