How can I use EnumWindows to find windows with a specific caption/title?

user1618236 picture user1618236 · Nov 8, 2013 · Viewed 37.8k times · Source

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.

Answer

zastrowm picture zastrowm · Nov 29, 2013

Original Answer

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");

Win32Interop.WinHandles

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"));