I am working on an application that will eventually be an api for driving UI Tests for a WPF application.
At one point of the initial test we are working on, we get 2 Windows security popups. We have some code that loops 10 times, it gets the handle of one of the popups using the FindWindowByCaption method and enters the information and clicks ok.
9 times out of 10 this works just fine, however we are occasionally seeing what looks to be a race condition. My suspicion is that the loop starts when only one of the windows is open and while its entering the information the second one opens and steals focus; after this it just hangs indefinitely.
What I'm wondering is if there is any method to get all of the window handles for a given caption, so that we can wait until there are 2 before starting the loop.
Use EnumWindows
and enumerate through all the windows, using GetWindowText
to get each window's text, then filter it however you want.
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
// Delegate to filter which windows to include
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
/// <summary> Get the text for the window pointed to by hWnd </summary>
public static string GetWindowText(IntPtr hWnd)
{
int size = GetWindowTextLength(hWnd);
if (size > 0)
{
var builder = new StringBuilder(size + 1);
GetWindowText(hWnd, builder, builder.Capacity);
return builder.ToString();
}
return String.Empty;
}
/// <summary> Find all windows that match the given filter </summary>
/// <param name="filter"> A delegate that returns true for windows
/// that should be returned and false for windows that should
/// not be returned </param>
public static IEnumerable<IntPtr> FindWindows(EnumWindowsProc filter)
{
IntPtr found = IntPtr.Zero;
List<IntPtr> windows = new List<IntPtr>();
EnumWindows(delegate(IntPtr wnd, IntPtr param)
{
if (filter(wnd, param))
{
// only add the windows that pass the filter
windows.Add(wnd);
}
// but return true here so that we iterate all windows
return true;
}, IntPtr.Zero);
return windows;
}
/// <summary> Find all windows that contain the given title text </summary>
/// <param name="titleText"> The text that the window title must contain. </param>
public static IEnumerable<IntPtr> FindWindowsWithText(string titleText)
{
return FindWindows(delegate(IntPtr wnd, IntPtr param)
{
return GetWindowText(wnd).Contains(titleText);
});
}
For example, to get all of the windows with "Notepad" in the title:
var windows = FindWindowsWithText("Notepad");
This answer proved popular enough that I created an OSS project, Win32Interop.WinHandles to provide an abstraction over IntPtrs for win32 windows. Using the library, to get all of the windows that contains "Notepad" in the title:
var allNotepadWindows
= TopLevelWindowUtils.FindWindows(wh => wh.GetWindowText().Contains("Notepad"));