WindowEnumerator.cs 4.7 KB

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