Я пишу простую структуру для тестирования приложений WinForm, которые запущены в процессе. Я использую Application.OpenForms, чтобы найти элемент управления, который мне нужно изменить на текущем шаге. Тем не менее, иногда я получаю ошибки, связанные с изменением коллекции (возможно, некоторым другим потоком). Как заблокировать эту коллекцию, чтобы убедиться, что обычные потоки пользовательского интерфейса (открытие новых форм и т. Д.) Не мешают?Как заблокировать Application.OpenForms?
ответ
Я не думаю, что есть встроенное решение, чтобы зафиксировать это свойство. Я предлагаю не использовать foreach
-loop, чтобы предотвратить исключение, вызванное тем, что итератор нашел модифицированную коллекцию. Просто используйте while
-loop (не for
-loop, потому что условие прерывания оценивается только один раз).
По-прежнему существует риск того, что форма удалена из списка после того, как вы проверили, что есть другая форма для обработки, и вы получите 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++;
}
Это далеко не идеальный вариант, но на данный момент я не могу представить лучшего решения. Например, вы можете быть в форме пять, приостановить, и приложение закрывает некоторые из первых пяти форм. Вследствие этого необработанные формы перемещаются вперёд и, следовательно, игнорируются кодом, когда поток продолжается в форме шесть.
Если вам нужно обработать все формы в какой-то момент (например, снимок состояния приложения), это может быть единственное решение для использования -loop и повторить попытку, пока вам не удастся выполнить итерацию по всей коллекции без исключение. Конечно, существует очевидный риск того, что это никогда не произойдет, если формы открыты и закрыты с очень высокими темпами или в зависимости от вашего итерационного кода.
Связанный раствор не является потокобезопасным; он просто избегает проблемы изменения коллекции при перечислении ее в том же потоке. –
Забудьте о 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);
Его вряд ли я хотел, но кажется, что нет хорошего решения. Приветствия. – Grzenio