一尘不染

如何使用EnumWindows查找带有特定标题/标题的窗口?

c#

我正在开发一个最终将成为驱动WPF应用程序的UI测试的api的应用程序。

在我们正在进行的初始测试的某一时刻,我们得到2个Windows安全性弹出窗口。我们有一些循环执行10次的代码,它使用FindWindowByCaption方法获取其中一个弹出窗口的句柄,然后输入信息并单击“确定”。

10的9倍可以正常工作,但是我们偶尔会看到看起来像是种族的情况。我的怀疑是,仅当其中一个窗口打开时,循环才开始,而在输入信息时,第二个窗口打开并窃取焦点。之后,它会无限期地挂起。

我想知道的是,是否有任何方法可以获取给定标题的所有窗口句柄,以便我们可以等到2个之后才能开始循环。


阅读 963

收藏
2020-05-19

共1个答案

一尘不染

原始答案

使用EnumWindows并枚举所有窗口,使用GetWindowText来获取每个窗口的文本,然后根据需要对其进行过滤。

[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);
    });
}

例如,要获得所有标题中带有“记事本”的窗口:

var windows = FindWindowsWithText("Notepad");

Win32Interop.WinHandles

这个答案非常受欢迎,以至于我创建了一个OSS项目Win32Interop.WinHandles,以为Win32
Windows提供基于IntPtrs的抽象。使用该库,获取标题中包含“记事本”的所有窗口:

var allNotepadWindows
   = TopLevelWindowUtils.FindWindows(wh => wh.GetWindowText().Contains("Notepad"));
2020-05-19