C#调用Windows API获取可见窗口却返回不可见窗口的问题
解决C#获取可见应用窗口时过滤系统后台窗口的问题
你遇到的Microsoft Text Input Application是输入法相关的后台窗口,Program Manager是桌面窗口,它们虽然通过IsWindowVisible校验,但不属于用户主动打开的应用窗口。要过滤掉这类系统窗口,需要在现有判断逻辑中补充窗口类名检查和窗口扩展样式验证。
步骤1:补充必要的Windows API声明
在你的类中添加以下DllImport声明:
using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Runtime.InteropServices; public static class WindowHelper { private const int BufferSize = 256; private const int GW_HWNDNEXT = 2; private const int GWL_EXSTYLE = -20; private const int WS_EX_APPWINDOW = 0x00040000; private const int WS_EX_TOOLWINDOW = 0x00000080; [DllImport("user32.dll")] private static extern IntPtr GetTopWindow(IntPtr hWnd); [DllImport("user32.dll")] private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowTextA(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32.dll")] private static extern bool GetWindowRect(IntPtr hWnd, ref Rect lpRect); [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] private static extern bool IsIconic(IntPtr hWnd); // 新增:获取窗口类名 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); // 新增:获取窗口扩展样式 [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); // 假设你的DesktophWnd和hWnd已定义 private static IntPtr DesktophWnd = IntPtr.Zero; private static IntPtr hWnd = IntPtr.Zero; public struct Rect { public int Left; public int Top; public int Right; public int Bottom; } public class Window { public IntPtr Handle { get; } public string Title { get; } public Rectangle Bounds { get; } public int Layer { get; } public Rect RawRect { get; } public Window(IntPtr handle, string title, Rectangle bounds, int layer, Rect rawRect) { Handle = handle; Title = title; Bounds = bounds; Layer = layer; RawRect = rawRect; } }
步骤2:修改GetWindows方法的过滤逻辑
更新判断条件,排除系统窗口并验证应用窗口属性:
public static List<Window> GetWindows() { List<Window> AllWindows = new List<Window>(); IntPtr WindowHandle = GetTopWindow(DesktophWnd); int LayerIndex = 1; while (WindowHandle != IntPtr.Zero) { StringBuilder titleBuffer = new StringBuilder(BufferSize); GetWindowTextA(WindowHandle, titleBuffer, BufferSize); string windowTitle = titleBuffer.ToString(); Rect R = new Rect(); GetWindowRect(WindowHandle, ref R); // 获取窗口类名 StringBuilder classNameBuffer = new StringBuilder(BufferSize); GetClassName(WindowHandle, classNameBuffer, classNameBuffer.Capacity); string className = classNameBuffer.ToString(); // 获取窗口扩展样式 long exStyle = GetWindowLongPtr(WindowHandle, GWL_EXSTYLE).ToInt64(); // 过滤系统窗口:排除桌面窗口(Progman)、输入法窗口(IME/Microsoft Text Input Application) bool isSystemWindow = className.Equals("Progman", StringComparison.OrdinalIgnoreCase) || className.Equals("IME", StringComparison.OrdinalIgnoreCase) || className.Equals("Microsoft Text Input Application", StringComparison.OrdinalIgnoreCase); // 判断是否为用户可见的应用窗口: // 1. 具备WS_EX_APPWINDOW样式(会显示在任务栏) // 2. 不是工具窗口且有标题(部分应用窗口可能没有WS_EX_APPWINDOW但仍为可见应用) bool isAppWindow = (exStyle & WS_EX_APPWINDOW) != 0 || ((exStyle & WS_EX_TOOLWINDOW) == 0 && !string.IsNullOrEmpty(windowTitle)); if (IsWindowVisible(WindowHandle) && !isSystemWindow && isAppWindow && WindowHandle != hWnd && !IsIconic(WindowHandle)) { var Rect = new Rectangle(R.Left, R.Top, R.Right - R.Left, R.Bottom - R.Top); Window W = new Window(WindowHandle, windowTitle, Rect, LayerIndex, R); AllWindows.Add(W); LayerIndex++; Console.WriteLine($"Title:{W.Title} Bounds:{W.Bounds} Layer:{W.Layer}"); } WindowHandle = GetWindow(WindowHandle, GW_HWNDNEXT); } return AllWindows; } }
关键过滤逻辑说明
- 类名过滤:直接排除已知的系统窗口类名(
Progman、IME等),避免桌面和输入法后台窗口被误判。 - 扩展样式验证:
WS_EX_APPWINDOW:标记窗口为应用主窗口,会显示在任务栏,是最准确的应用窗口标识。- 补充判断非工具窗口且有标题:覆盖部分没有
WS_EX_APPWINDOW但仍为用户可见的应用窗口场景。
内容的提问来源于stack exchange,提问作者Nairel Prandini
相关产品推荐
相关产品推荐

