原文:Windows 系统上用 .NET/C# 查找所有窗口,并获得窗口的标题、位置、尺寸、最小化、可见性等各种状态

在 Windows 应用开发中,如果需要操作其他的窗口,那么可以使用 EnumWindows 这个 API 来枚举这些窗口。

你可以使用本文编写的一个类型,查找到所有窗口中你关心的信息。


需要使用的 API

枚举所有窗口仅需要使用到 EnumWindows,其中需要定义一个委托 WndEnumProc 作为传入参数的类型。

剩下的我们需要其他各种方法用于获取窗口的其他属性。

private delegate bool WndEnumProc(IntPtr hWnd, int lParam);

[DllImport("user32")]
private static extern bool EnumWindows(WndEnumProc lpEnumFunc, int lParam); [DllImport("user32")]
private static extern IntPtr GetParent(IntPtr hWnd); [DllImport("user32")]
private static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lptrString, int nMaxCount); [DllImport("user32")]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount); [DllImport("user32")]
private static extern bool GetWindowRect(IntPtr hWnd, ref LPRECT rect); [StructLayout(LayoutKind.Sequential)]
private readonly struct LPRECT
{
public readonly int Left;
public readonly int Top;
public readonly int Right;
public readonly int Bottom;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

枚举所有窗口

我将以上 API 封装成 FindAll 函数,并提供过滤器可以给大家过滤众多的窗口使用。

比如,我写了下面一个简单的示例,可以输出当前可见的所有窗口以及其位置和尺寸:

using System;

namespace Walterlv.WindowDetector
{
class Program
{
static void Main(string[] args)
{
var windows = WindowEnumerator.FindAll();
for (int i = 0; i < windows.Count; i++)
{
var window = windows[i];
Console.WriteLine($@"{i.ToString().PadLeft(3, ' ')}. {window.Title}
{window.Bounds.X}, {window.Bounds.Y}, {window.Bounds.Width}, {window.Bounds.Height}");
}
Console.ReadLine();
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里的 FindAll 方法,我提供了一个默认参数,可以指定如何过滤所有枚举到的窗口。如果不指定,则会找可见的,包含标题的,没有最小化的窗口。如果你希望找一些看不见的窗口,可以自己写过滤条件。

什么都不要过滤的话,就传入 _ => true,意味着所有的窗口都会被枚举出来。

附源码

因为源代码会经常更新,所以建议在这里查看:

无法访问的话,可以看下面:

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

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

发布了382 篇原创文章 · 获赞 232 · 访问量 47万+

Windows 系统上用 .NET/C# 查找所有窗口,并获得窗口的标题、位置、尺寸、最小化、可见性等各种状态的更多相关文章

  1. windows系统上安装与使用Android NDK r5 (转)

    windows系统上安装与使用Android NDK r5  很早就听说了android的NDK应用,只是一直没有时间去研究,今天花了点时间在windows平台搭建了NDK环境,并成功运行了第一个简单 ...

  2. spm完成dmp在windows系统上导入详细过程

    --查询dmp字符集 cat spmprd_20151030.dmp ','xxxx')) from dual; spm完成dmp在windows系统上导入详细过程 create tablespace ...

  3. 快速获取Windows系统上的国家和地区信息

    Windows系统上包含了200多个国家和地区的数据,有时候编程需要这些资料.以下代码可以帮助你快速获取这些信息.将Console语句注释掉,可以更快的完成分析. static void Main(s ...

  4. Windows系统上如何使用SSH

    Windows系统上如何使用SSH 传统的网络服务程序如FTP.Telnet等,在网络上一般使用明文传送数据.用户账号和口令信息,容易受到中间人的攻击.用户利用SSH协议后能有效防止DNS及IP欺骗, ...

  5. 如何在Windows系统上用抓包软件Wireshark截获iPhone等网络通讯数据

    http://www.jb51.net/os/windows/189090.html 今天给大家介绍一种如何在Windows操作系统上使用著名的抓包工具软件Wireshark来截获iPhone.iPa ...

  6. Redis进阶实践之三如何在Windows系统上安装安装Redis

    一.Redis的简介        Redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合 ...

  7. 非Unicode编码的软件如何在Windows系统上运行

    我们常常会遇到这样一种情况:点开某些日文软件(我不会说就是galgame( ╯□╰ ))会出现乱码或者直接无法运行. 出现乱码的原因很简单:编码与译码的方式不一致!!!!!!!!!!! 首先大家需要知 ...

  8. 在windows系统上使用pip命令安装python的第三方库

    在windows系统上使用pip命令安装python的第三方库 通过cmd启动命令行后,直接输入pip命令,有时候命令行会提示我们pip不是一个指令,这个时候我们可以通过python的集成开发环境里面 ...

  9. 在Windows系统上一批可以下载但是需要经过编译再安装的第三方的直接编译后的版本(UCI页面)

    在Windows系统上一批可以下载但是需要经过编译再安装的第三方的直接编译后的版本(UCI页面) (https://www.lfd.uci.edu/~gohlke/pythonlibs/) win10 ...

随机推荐

  1. Eclipse Maven问题小记

    Eclipse Maven Web工程报错:java.lang.ClassNotFoundException: ContextLoaderListener 原因:打包项目时没有把相关Maven依赖包打 ...

  2. GoCN每日新闻(2019-10-20)

    GoCN每日新闻(2019-10-20) slakc是如何构建共享频道的 https://slack.engineering/how-slack-built-shared-channels-8d42c ...

  3. 微信公众号_Deejo说_2019

    说明: 1. 文中的内容均来自Deejo说微信公众号 2. 微信中搜索"Deejo说"公众号,可关注 麻麻英语 ——2019.09.10—— It’s my treat. 我来请客 ...

  4. SpringMVC自定义类型转换器

    SpringMVC 自定义类型转换器  我们在使用SpringMVC时,常常需要把表单中的参数映射到我们对象的属性中,我们可以在默认的spring-servlet.xml加上如下的配置即可做到普通数据 ...

  5. Shell脚本实现对文件编辑

    常见Linux文件的编辑命令 vi/vim,有时候我们想写一个脚本实现对文件编辑,这个时候,可能就不够用了,下面介绍一些办法 1.echo命令 Shell的echo命令常用于字符串的输出 例如: [r ...

  6. 关于SkyApm测试部署。

    这个是skyapm的github : https://github.com/SkyAPM/SkyAPM-dotnet 它依赖于skywalking . 我是用docker去部署的.因为这样我的系统会干 ...

  7. 快速激活Navicat Premium 12

    Navicat Premium 12是一套数据库开发管理工具,支持链家MySQL.Oracle.SQL server等多种数据库,快速便捷创建.管理和维护数据库 一.下载 https://www.na ...

  8. android -------- 流式布局,支持单选、多选等

    最近开发中有流式标签这个功能,网上学了下,来分享一下 Android 流式布局,支持单选.多选等,适合用于产品标签等. 效果图: 用法: dependencies { compile 'com.hym ...

  9. tomcat请求响应代码分享

    NioEndpoint的Poller轮询器持续进行扫描是否有新的event()方法调用后产生新的event配置. 发现后执行AbstractProtocol.class中的process()方法进行处 ...

  10. Logstash配置同步mysql到es配置

    关于logstash安装:https://www.cnblogs.com/toov5/p/10301727.html Logstash是一个开源数据收集引擎,具有实时管道功能.Logstash可以动态 ...