25

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

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

10 次中有 9 次可以正常工作,但是我们偶尔会看到看起来像是比赛条件的情况。我的怀疑是当只有一个窗口打开时循环开始,当它输入信息时,第二个窗口打开并窃取焦点;在此之后它只是无限期地挂起。

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

4

3 回答 3

94

原始答案

使用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 窗口提供对 IntPtrs 的抽象。使用该库,获取标题中包含“记事本”的所有窗口:

var allNotepadWindows
   = TopLevelWindowUtils.FindWindows(wh => wh.GetWindowText().Contains("Notepad"));
于 2013-11-29T00:35:50.153 回答
0

我知道这是一个老问题,但随着 Visual Studio 走向未来,答案会随着时间而改变。

我想分享我的解决方案,它允许您搜索部分窗口标题,当标题标题包含不可预测的文本时,这通常是需要的。例如,如果您想查找 Windows Mail 应用程序的句柄,标题将包含文本“收件箱 - yourremailaccountname”。显然,您不想对帐户名称进行硬编码。这是我的代码,虽然它在 Visual Basic .NET 中,但您可以将其转换为 C#。输入部分标题(即“收件箱-”),单击按钮,您将得到 hwnd 和完整标题。我尝试使用 Process.GetProcesses() 但与 Win API 相比它的速度很慢。

此示例将在 EnumWindows 调用的 lparm 中返回您搜索的窗口句柄(第二个参数由 ref 传递),即使应用程序已最小化,也会将其置于最前面。

Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> Private Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsProcDelegate, ByRef lParam As IntPtr) As Boolean
    End Function
    Private Delegate Function EnumWindowsProcDelegate(ByVal hWnd As IntPtr, ByRef lParam As IntPtr) As Integer

    <DllImport("user32.dll")>
    Private Shared Function GetWindowTextLength(ByVal hWnd As IntPtr) As Integer
    End Function

    <DllImport("user32.dll")>
    Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
    End Function

    <DllImport("user32", EntryPoint:="SendMessageA", CharSet:=CharSet.Ansi, SetLastError:=True, ExactSpelling:=True)> Public Shared Function SendMessage(ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByRef lParam As Integer) As Integer
    End Function

    <DllImport("user32.dll")>
    Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function SetActiveWindow(ByVal hWnd As IntPtr) As Integer
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function SetWindowPos(ByVal hWnd As IntPtr, hWndInsertAfter As IntPtr, x As Integer, y As Integer, cx As Integer, cy As Integer, uFlags As UInt32) As Boolean
    End Function

    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function RedrawWindow(ByVal hWnd As IntPtr, lprcUpdate As Integer, hrgnUpdate As Integer, uFlags As UInt32) As Boolean
    End Function

    Public Const WM_SYSCOMMAND As Integer = &H112
    Public Const SC_RESTORE = &HF120
    Public Const SWP_SHOWWINDOW As Integer = &H40
    Public Const SWP_NOSIZE As Integer = &H1
    Public Const SWP_NOMOVE As Integer = &H2
    Public Const RDW_FRAME As Int32 = 1024 'Updates the nonclient area if included in the redraw area. RDW_INVALIDATE must also be specified.
    Public Const RDW_INVALIDATE As Int32 = 1 'Invalidates the redraw area.
    Public Const RDW_ALLCHILDREN As Int32 = 128 'Redraw operation includes child windows if present in the redraw area.

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim strPartialTitle As String = TextBox1.Text
        Dim intptrByRefFoundHwnd As IntPtr = Marshal.StringToHGlobalAnsi(strPartialTitle)
        Dim delegateEnumWindowsProcDelegate As EnumWindowsProcDelegate
        delegateEnumWindowsProcDelegate = New EnumWindowsProcDelegate(AddressOf EnumWindowsProc)
        EnumWindows(delegateEnumWindowsProcDelegate, intptrByRefFoundHwnd)
        LabelHwndAndWindowTitle.Text = intptrByRefFoundHwnd
        BringWindowToFront(intptrByRefFoundHwnd)
    End Sub

    Function EnumWindowsProc(ByVal hWnd As IntPtr, ByRef lParam As IntPtr) As Integer
        Dim strPartialTitle As String = Marshal.PtrToStringAnsi(lParam)
        Dim length As Integer = GetWindowTextLength(hWnd)
        Dim stringBuilder As New StringBuilder(length)
        GetWindowText(hWnd, stringBuilder, (length + 1))
        If stringBuilder.ToString.Trim.Length > 2 Then
            If stringBuilder.ToString.ToLower.Contains(strPartialTitle.ToLower) Then
                Debug.WriteLine(hWnd.ToString & ": " & stringBuilder.ToString)
                lParam = hWnd ' Pop hwnd to top, returns in lParm of EnumWindows Call (2nd parameter)
                Return False
            End If
        End If
        Return True
    End Function

    Private Sub BringWindowToFront(hwnd As IntPtr)
        SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0) ' restore the minimize window
        SetForegroundWindow(hwnd)
        SetActiveWindow(hwnd)
        SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_SHOWWINDOW Or SWP_NOMOVE Or SWP_NOSIZE)
        'redraw to prevent the window blank.
        RedrawWindow(hwnd, IntPtr.Zero, 0, RDW_FRAME Or RDW_INVALIDATE Or RDW_ALLCHILDREN)
    End Sub

End Class
于 2019-01-26T03:37:01.433 回答
-1
using HWND = IntPtr;

/// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
/// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
/// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
public static IDictionary<HWND, string> GetOpenWindows()
{
HWND shellWindow = GetShellWindow();
Dictionary<HWND, string> windows = new Dictionary<HWND, string>();

EnumWindows(delegate(HWND hWnd, int lParam)
{
  if (hWnd == shellWindow) return true;
  if (!IsWindowVisible(hWnd)) return true;

  int length = GetWindowTextLength(hWnd);
  if (length == 0) return true;

  StringBuilder builder = new StringBuilder(length);
  GetWindowText(hWnd, builder, length + 1);

  windows[hWnd] = builder.ToString();
  return true;

}, 0);

return windows;
}

private delegate bool EnumWindowsProc(HWND hWnd, int lParam);

[DllImport("USER32.DLL")]
private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

[DllImport("USER32.DLL")]
private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);

[DllImport("USER32.DLL")]
private static extern int GetWindowTextLength(HWND hWnd);

[DllImport("USER32.DLL")]
private static extern bool IsWindowVisible(HWND hWnd);

[DllImport("USER32.DLL")]
private static extern IntPtr GetShellWindow();
}

这是一些使用它的代码:

foreach(KeyValuePair<IntPtr, string> window in OpenWindowGetter.GetOpenWindows())
{
IntPtr handle = window.Key;
string title = window.Value;

Console.WriteLine("{0}: {1}", handle, title);
}

我从http://www.tcx.be/blog/2006/list-open-windows/得到这段代码

如果您需要有关如何使用它的帮助,请告诉我,我想通了

于 2015-10-09T13:51:50.517 回答