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