Walterlv.WindowDetector.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. namespace Walterlv.WindowDetector
  7. {
  8. /// <summary>
  9. /// 包含枚举当前用户空间下所有窗口的方法。
  10. /// </summary>
  11. public class WindowEnumerator
  12. {
  13. /// <summary>
  14. /// 查找当前用户空间下所有符合条件的窗口。如果不指定条件,将仅查找可见窗口。
  15. /// </summary>
  16. /// <param name="match">过滤窗口的条件。如果设置为 null,将仅查找可见窗口。</param>
  17. /// <returns>找到的所有窗口信息。</returns>
  18. public static IReadOnlyList<WindowInfo> FindAll(Predicate<WindowInfo> match = null)
  19. {
  20. var windowList = new List<WindowInfo>();
  21. EnumWindows(OnWindowEnum, 0);
  22. return windowList.FindAll(match ?? DefaultPredicate);
  23. bool OnWindowEnum(IntPtr hWnd, int lparam)
  24. {
  25. // 仅查找顶层窗口。
  26. if (GetParent(hWnd) == IntPtr.Zero)
  27. {
  28. // 获取窗口类名。
  29. var lpString = new StringBuilder(512);
  30. GetClassName(hWnd, lpString, lpString.Capacity);
  31. var className = lpString.ToString();
  32. // 获取窗口标题。
  33. var lptrString = new StringBuilder(512);
  34. GetWindowText(hWnd, lptrString, lptrString.Capacity);
  35. var title = lptrString.ToString().Trim();
  36. // 获取窗口可见性。
  37. var isVisible = IsWindowVisible(hWnd);
  38. // 获取窗口位置和尺寸。
  39. LPRECT rect = default;
  40. GetWindowRect(hWnd, ref rect);
  41. var bounds = new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
  42. // 添加到已找到的窗口列表。
  43. windowList.Add(new WindowInfo(hWnd, className, title, isVisible, bounds));
  44. }
  45. return true;
  46. }
  47. }
  48. /// <summary>
  49. /// 显示窗口最前面
  50. /// </summary>
  51. /// <param name="hWnd"></param>
  52. /// <returns></returns>
  53. public static void ForceWindowIntoForeground(IntPtr window)
  54. {
  55. int currentThread = GetCurrentThreadId();
  56. int activeWindow = GetForegroundWindow();
  57. int activeThread = GetWindowThreadProcessId((IntPtr)activeWindow, out int activeProcess);
  58. int windowThread = GetWindowThreadProcessId(window, out int windowProcess);
  59. if (currentThread != activeThread)
  60. AttachThreadInput(currentThread, activeThread, true);
  61. if (windowThread != currentThread)
  62. AttachThreadInput(windowThread, currentThread, true);
  63. int oldTimeout = 0, newTimeout = 0;
  64. SystemParametersInfo(0x2000, 0, ref oldTimeout, 0);
  65. SystemParametersInfo(0x2000, 0, ref newTimeout, 0);
  66. LockSetForegroundWindow(2);
  67. AllowSetForegroundWindow(-1);
  68. SetForegroundWindow(window);
  69. ShowWindow(window, 9);
  70. SystemParametersInfo(0x2000, 0, ref oldTimeout, 0);
  71. if (currentThread != activeThread)
  72. AttachThreadInput(currentThread, activeThread, false);
  73. if (windowThread != currentThread)
  74. AttachThreadInput(windowThread, currentThread, false);
  75. }
  76. /// <summary>
  77. /// 默认的查找窗口的过滤条件。可见 + 非最小化 + 包含窗口标题。
  78. /// </summary>
  79. private static readonly Predicate<WindowInfo> DefaultPredicate = x => x.IsVisible && x.Title.Length > 0;
  80. private delegate bool WndEnumProc(IntPtr hWnd, int lParam);
  81. [DllImport("user32")]
  82. private static extern bool EnumWindows(WndEnumProc lpEnumFunc, int lParam);
  83. [DllImport("user32")]
  84. private static extern IntPtr GetParent(IntPtr hWnd);
  85. [DllImport("user32")]
  86. private static extern bool IsWindowVisible(IntPtr hWnd);
  87. [DllImport("user32")]
  88. private static extern int GetWindowText(IntPtr hWnd, StringBuilder lptrString, int nMaxCount);
  89. [DllImport("user32")]
  90. private static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
  91. [DllImport("user32")]
  92. private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
  93. [DllImport("user32")]
  94. private static extern bool GetWindowRect(IntPtr hWnd, ref LPRECT rect);
  95. [DllImport("user32 ")]
  96. public static extern long SetWindowPos(IntPtr hwnd, long hWndInsertAfter, long X, long y, long cx, long cy, long wFlagslong);
  97. [DllImport("user32.dll")]
  98. internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
  99. [DllImport("User32.dll")]
  100. private static extern bool SetForegroundWindow(IntPtr hWnd);
  101. [DllImport("User32.dll")]
  102. private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
  103. [DllImport("user32.dll")]
  104. private static extern bool SetWindowPos(IntPtr hWnd, int hWndlnsertAfter, int X, int Y, int cx, int cy, uint Flags);
  105. [DllImport("user32.dll", EntryPoint = "SystemParametersInfo")]
  106. public static extern int SystemParametersInfo(int uAction, int uParam, IntPtr lpvParam, int fuWinIni);
  107. [DllImport("User32.dll", SetLastError = true)]
  108. public static extern int GetForegroundWindow();
  109. [DllImport("User32.dll", CharSet = CharSet.Auto)]
  110. public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);
  111. [DllImport("User32.dll", SetLastError = true)]
  112. private static extern int AttachThreadInput(int CurrentForegroundThread, int MakeThisThreadForegrouond, bool boolAttach);
  113. [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
  114. public static extern int GetCurrentThreadId();
  115. [DllImport("user32", CharSet = CharSet.Auto)]
  116. static extern bool SystemParametersInfo(int uiAction, int uiParam, ref int pvParam, int fWinIni);
  117. [DllImport("user32", CharSet = CharSet.Auto)]
  118. static extern bool LockSetForegroundWindow(int a);
  119. [DllImport("user32", CharSet = CharSet.Auto)]
  120. static extern bool AllowSetForegroundWindow(int a);
  121. [StructLayout(LayoutKind.Sequential)]
  122. private readonly struct LPRECT
  123. {
  124. public readonly int Left;
  125. public readonly int Top;
  126. public readonly int Right;
  127. public readonly int Bottom;
  128. }
  129. }
  130. /// <summary>
  131. /// 获取 Win32 窗口的一些基本信息。
  132. /// </summary>
  133. public readonly struct WindowInfo
  134. {
  135. public WindowInfo(IntPtr hWnd, string className, string title, bool isVisible, Rectangle bounds) : this()
  136. {
  137. Hwnd = hWnd;
  138. ClassName = className;
  139. Title = title;
  140. IsVisible = isVisible;
  141. Bounds = bounds;
  142. }
  143. /// <summary>
  144. /// 获取窗口句柄。
  145. /// </summary>
  146. public IntPtr Hwnd { get; }
  147. /// <summary>
  148. /// 获取窗口类名。
  149. /// </summary>
  150. public string ClassName { get; }
  151. /// <summary>
  152. /// 获取窗口标题。
  153. /// </summary>
  154. public string Title { get; }
  155. /// <summary>
  156. /// 获取当前窗口是否可见。
  157. /// </summary>
  158. public bool IsVisible { get; }
  159. /// <summary>
  160. /// 获取窗口当前的位置和尺寸。
  161. /// </summary>
  162. public Rectangle Bounds { get; }
  163. /// <summary>
  164. /// 获取窗口当前是否是最小化的。
  165. /// </summary>
  166. public bool IsMinimized => Bounds.Left == -32000 && Bounds.Top == -32000;
  167. }
  168. }