You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
    }
}

关键过滤逻辑说明

  1. 类名过滤:直接排除已知的系统窗口类名(Progman、IME等),避免桌面和输入法后台窗口被误判。
  2. 扩展样式验证:
    • WS_EX_APPWINDOW:标记窗口为应用主窗口,会显示在任务栏,是最准确的应用窗口标识。
    • 补充判断非工具窗口且有标题:覆盖部分没有WS_EX_APPWINDOW但仍为用户可见的应用窗口场景。

内容的提问来源于stack exchange,提问作者Nairel Prandini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:15:35