WindowEnumerator.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. /// <summary>
  7. /// 包含枚举当前用户空间下所有窗口的方法。
  8. /// </summary>
  9. public class WindowEnumerator
  10. {
  11. /// <summary>
  12. /// 查找当前用户空间下所有符合条件的窗口。如果不指定条件,将仅查找可见窗口。
  13. /// </summary>
  14. /// <param name="match">过滤窗口的条件。如果设置为 null,将仅查找可见窗口。</param>
  15. /// <returns>找到的所有窗口信息。</returns>
  16. public static IReadOnlyList<WindowInfo> FindAll(Predicate<WindowInfo> match = null)
  17. {
  18. var windowList = new List<WindowInfo>();
  19. EnumWindows(OnWindowEnum, 0);
  20. return windowList.FindAll(match ?? DefaultPredicate);
  21. bool OnWindowEnum(IntPtr hWnd, int lparam)
  22. {
  23. // 仅查找顶层窗口。
  24. if (GetParent(hWnd) == IntPtr.Zero)
  25. {
  26. // 获取窗口类名。
  27. var lpString = new StringBuilder(512);
  28. GetClassName(hWnd, lpString, lpString.Capacity);
  29. var className = lpString.ToString();
  30. // 获取窗口标题。
  31. var lptrString = new StringBuilder(512);
  32. GetWindowText(hWnd, lptrString, lptrString.Capacity);
  33. var title = lptrString.ToString().Trim();
  34. // 获取窗口可见性。
  35. var isVisible = IsWindowVisible(hWnd);
  36. // 获取窗口位置和尺寸。
  37. LPRECT rect = default;
  38. GetWindowRect(hWnd, ref rect);
  39. var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
  40. // 添加到已找到的窗口列表。
  41. windowList.Add(new WindowInfo(hWnd, className, title, isVisible, bounds));
  42. }
  43. return true;
  44. }
  45. }
  46. /// <summary>
  47. /// 默认的查找窗口的过滤条件。可见 + 非最小化 + 包含窗口标题。
  48. /// </summary>
  49. private static readonly Predicate<WindowInfo> DefaultPredicate = x => x.IsVisible && !x.IsMinimized && x.Title.Length > 0;
  50. private delegate bool WndEnumProc(IntPtr hWnd, int lParam);
  51. [DllImport("user32")]
  52. private static extern bool EnumWindows(WndEnumProc lpEnumFunc, int lParam);
  53. [DllImport("user32")]
  54. private static extern IntPtr GetParent(IntPtr hWnd);
  55. [DllImport("user32")]
  56. private static extern bool IsWindowVisible(IntPtr hWnd);
  57. [DllImport("user32")]
  58. private static extern int GetWindowText(IntPtr hWnd, StringBuilder lptrString, int nMaxCount);
  59. [DllImport("user32")]
  60. private static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
  61. [DllImport("user32")]
  62. private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
  63. [DllImport("user32")]
  64. private static extern bool GetWindowRect(IntPtr hWnd, ref LPRECT rect);
  65. [StructLayout(LayoutKind.Sequential)]
  66. private readonly struct LPRECT
  67. {
  68. public readonly int Left;
  69. public readonly int Top;
  70. public readonly int Right;
  71. public readonly int Bottom;
  72. }
  73. }
  74. /// <summary>
  75. /// 获取 Win32 窗口的一些基本信息。
  76. /// </summary>
  77. public readonly struct WindowInfo
  78. {
  79. public WindowInfo(IntPtr hWnd, string className, string title, bool isVisible, Rectangle bounds) : this()
  80. {
  81. Hwnd = hWnd;
  82. ClassName = className;
  83. Title = title;
  84. IsVisible = isVisible;
  85. Bounds = bounds;
  86. }
  87. /// <summary>
  88. /// 获取窗口句柄。
  89. /// </summary>
  90. public IntPtr Hwnd { get; }
  91. /// <summary>
  92. /// 获取窗口类名。
  93. /// </summary>
  94. public string ClassName { get; }
  95. /// <summary>
  96. /// 获取窗口标题。
  97. /// </summary>
  98. public string Title { get; }
  99. /// <summary>
  100. /// 获取当前窗口是否可见。
  101. /// </summary>
  102. public bool IsVisible { get; }
  103. /// <summary>
  104. /// 获取窗口当前的位置和尺寸。
  105. /// </summary>
  106. public Rectangle Bounds { get; }
  107. /// <summary>
  108. /// 获取窗口当前是否是最小化的。
  109. /// </summary>
  110. public bool IsMinimized => Bounds.Left == -32000 && Bounds.Top == -32000;
  111. }