简体   繁体   English

如何锁定Application.OpenForms?

[英]How can I lock Application.OpenForms?

I am writing a simple framework for testing WinForm apps, which is running in process. 我正在编写一个用于测试WinForm应用程序的简单框架,该框架正在运行中。 I am using Application.OpenForms to find the control I need to change in the current test step. 我正在使用Application.OpenForms查找在当前测试步骤中需要更改的控件。 However, I am sometimes getting errors that the collection was modified (probably by some other thread). 但是,有时我会收到错误的消息,说明该集合已被修改(可能是由其他线程修改)。 How can I lock this collection, to make sure that the normal UI threads (opening new forms, etc) are not interfering? 如何锁定此集合,以确保普通的UI线程(打开新表单等)不会受到干扰?

I don't think there is a build-in solution to lock this property. 我认为没有内置的解决方案可以锁定此属性。 I suggest not to use a foreach -loop in order to prevent the exception caused by the iterator finding a modified collection. 我建议不要使用foreach -loop来防止由迭代器找到修改后的集合导致的异常。 Just use a while -loop (not a for -loop because the breaking condition is only evaluated once). 只需使用while -loop(而不是for -loop,因为中断条件仅被评估一次)。

There is still the risk that a form is removed from the list after you checked that there is another form to be processed and you will get an ArgumentOutOfRangeException . 在检查是否还有其他表单要处理之后,仍然存在从列表中删除表单的风险,并且您将获得ArgumentOutOfRangeException you will have to handle this situation gracefully. 您将不得不优雅地处理这种情况。

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

This is far from perfect but I cannot think of a better solution at the moment. 这远非完美,但我目前无法想到更好的解决方案。 For example you could be at form five, get suspended, and the application closes some of the first five forms. 例如,您可能处于表格5中,被暂停,应用程序关闭前五个表格中的某些表格。 In consequence unprocessed forms are moved towards the front and in consequence are ignored by the code when the thread continues at form six. 结果,未处理的表单移到最前面,因此当线程继续到表单6时,代码将忽略它们。

If you have to process all forms open at some point (like a snapshot of the application state), it might actually be the only solution to use a foreach -loop and retry until you manage to iterate over the complete collection without an exception. 如果必须处理所有在某个时刻打开的表单(例如应用程序状态的快照),则实际上它可能是使用foreach -loop并重试直到设法无例外地遍历整个集合的唯一解决方案。 There is of course the obvious risk that this never happens if forms are opened and closed at very high rates or depending on your iterating code. 当然,如果以很高的速率或根据您的迭代代码打开和关闭表单,则很明显存在永远不会发生的风险。

Forget about Application.OpenForms and use unmanaged WinApi instead. 忘记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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM