如何锁定Application.OpenForms?

时间:2009-09-02 09:21:57

标签: c# .net winforms

我正在编写一个用于测试正在运行的WinForm应用程序的简单框架。我正在使用Application.OpenForms来查找我需要在当前测试步骤中更改的控件。但是,我有时会收到修改集合的错误(可能是由其他一些线程)。如何锁定此集合,以确保正常的UI线程(打开新表单等)不会干扰?

3 个答案:

答案 0 :(得分:2)

我认为没有内置解决方案来锁定此属性。我建议使用foreach - 循环,以防止迭代器查找修改后的集合导致异常。只需使用while - 循环(不是for - 循环,因为破坏条件仅评估一次。)

在您检查了要处理的其他表单后,仍然存在从列表中删除表单的风险,您将获得ArgumentOutOfRangeException。你必须优雅地处理这种情况。

Int32 index = 0;
while (index < Application.OpenForms.Count)
{
    try
    {
        // Try to copy the form because the index may be or may
        // become invalid.
        Form form = Application.OpenForms[index];

        // Do stuff with the form.
    }
    catch (ArgumentOutOfRangeException exception)
    {
        // Handle no longer valid index.
    }

    index++;
}

这远非完美,但我现在想不出更好的解决方案。例如,您可能处于表单五,被暂停,并且应用程序将关闭前五个表单中的一些表单。因此,未处理的表单会向前移动,因此当线程在表单6中继续时,代码会忽略这些表单。

如果必须在某个时刻处理所有表单(例如应用程序状态的快照),它实际上可能是使用foreach循环的唯一解决方案并重试,直到您设法迭代完整的收藏,没有例外。当然,如果以非常高的速率打开和关闭表单,或者根据您的迭代代码,这种情况不会发生。

答案 1 :(得分:1)

答案 2 :(得分:0)

忘掉Application.OpenForms并使用非托管的WinApi。

static IEnumerable<Form> EnumOpenForms()
{
    foreach (System.Diagnostics.ProcessThread thread in System.Diagnostics.Process.GetCurrentProcess().Threads)
    {
        List<IntPtr> hWnds = new List<IntPtr>();
        EnumThreadWindows(thread.Id, (hWnd, param) => { hWnds.Add(hWnd); }, IntPtr.Zero);
        foreach(IntPtr hWnd in hWnds)
        {
            Form form = Control.FromHandle(hWnd) as Form;
            if (form != null)
            {
                yield return form;
            }
        }
    }
}

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumWindowProc lpEnumFunc, IntPtr lParam);

delegate void EnumWindowProc(IntPtr hWnd, IntPtr parameter);