FFMPEG录屏(18)--- 枚举Windows下的窗口列表并获取进程图标、标题、缩略图等

news2024/10/25 15:14:44

在Windows中获取可进行屏幕共享捕获的窗口列表及其图标、缩略图

在Windows系统中,获取可进行屏幕共享捕获的窗口列表以及它们的图标和缩略图是一个复杂但有趣的过程。本文将详细介绍如何实现这一功能,涉及到的主要技术包括Windows API、C++编程和一些第三方库。

前置知识

在开始之前,您需要了解以下内容:

  1. Windows API:Windows API提供了大量的函数,用于与操作系统进行交互。
  2. C++编程:本文的示例代码使用C++编写。
  3. 第三方库:我们将使用libyuv库来处理图像缩放。

实现步骤

1. 包含必要的头文件

首先,我们需要包含一些必要的头文件,这些头文件提供了我们需要的函数和数据结构。

#include "base/devices/screen/desktop_geometry.h"
#include "base/devices/screen/enumerator.h"
#include "base/devices/screen/mouse_cursor.h"
#include "base/devices/screen/utils.h"
#include "base/devices/screen/win/capture_utils.h"
#include "base/devices/screen/win/cursor.h"
#include "base/devices/screen/win/scoped_object_gdi.h"
#include "base/log/logger.h"
#include "base/strings/string_trans.h"
#include "base/utils/win/version.h"

#include <libyuv/scale_argb.h>

#include <memory>
#include <string>

#include <stdlib.h>

#include <shellapi.h>
#include <windows.h>

2. 定义辅助函数

我们需要一些辅助函数来获取窗口属性、窗口文本、进程路径等。

获取窗口属性
typedef HRESULT(WINAPI *FuncDwmGetWindowAttribute)(HWND window, DWORD dwAttribute, PVOID pvAttribute, DWORD cbAttribute);

FuncDwmGetWindowAttribute helper_get_dwmapi_get_window_attribute() {
  HINSTANCE dwmapi = LoadLibraryW(L"Dwmapi.dll");
  if (dwmapi == nullptr) {
    return nullptr;
  }

  FuncDwmGetWindowAttribute dwmapi_get_window_attribute =
      (FuncDwmGetWindowAttribute)GetProcAddress(dwmapi, "DwmGetWindowAttribute");
  if (dwmapi_get_window_attribute == nullptr) {
    return nullptr;
  }

  return dwmapi_get_window_attribute;
}
获取窗口文本
int get_window_text_safe(HWND window, LPWSTR p_string, int cch_max_count) {
  return ::InternalGetWindowText(window, p_string, cch_max_count);
}
获取进程路径
int get_window_process_path(HWND window, wchar_t *path, int max_count) {
  DWORD process_id;
  ::GetWindowThreadProcessId(window, &process_id);
  if (process_id == 0) {
    return 0;
  }

  HANDLE process = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
  if (process == nullptr) {
    return 0;
  }

  DWORD buffer_size = static_cast<DWORD>(max_count);
  if (::QueryFullProcessImageNameW(process, 0, path, &buffer_size) == 0) {
    ::CloseHandle(process);
    return 0;
  }

  ::CloseHandle(process);
  return buffer_size;
}

3. 定义窗口枚举回调函数

我们需要一个回调函数来处理每个被枚举到的窗口。

BOOL WINAPI enum_screen_source_info_proc(HWND window, LPARAM lParam) {
  auto *param = reinterpret_cast<enumerator_param *>(lParam);

  if (!::IsWindowVisible(window) || ::IsIconic(window) || ::GetShellWindow() == window) {
    return TRUE;
  }

  if (::GetAncestor(window, GA_ROOT) != window) {
    return TRUE;
  }

  desktop_rect window_rect = desktop_rect::make_ltrb(0, 0, 0, 0);
  if (!get_window_rect(window, &window_rect) || window_rect.is_empty()) {
    return TRUE;
  }

  if (is_window_invisible_win10_background_app(window)) {
    return TRUE;
  }

  HWND owner = ::GetWindow(window, GW_OWNER);
  LONG exstyle = ::GetWindowLongW(window, GWL_EXSTYLE);
  if (owner && !(exstyle & WS_EX_APPWINDOW)) {
    return TRUE;
  }

  if ((exstyle & WS_EX_TOOLWINDOW) &&
      !(param->external_flags & TRAA_SCREEN_SOURCE_FLAG_NOT_IGNORE_TOOLWINDOW)) {
    return TRUE;
  }

  if (!capture_utils::is_window_response(window) &&
      !(param->external_flags & TRAA_SCREEN_SOURCE_FLAG_NOT_IGNORE_UNRESPONSIVE)) {
    return TRUE;
  }

  bool owned_by_current_process = capture_utils::is_window_owned_by_current_process(window);
  if ((param->external_flags & TRAA_SCREEN_SOURCE_FLAG_IGNORE_CURRENT_PROCESS) &&
      owned_by_current_process) {
    return TRUE;
  }

  bool has_title = false;
  WCHAR window_title[TRAA_MAX_DEVICE_NAME_LENGTH] = L"";
  if (get_window_text_safe(window, window_title, TRAA_MAX_DEVICE_NAME_LENGTH - 1) > 0) {
    has_title = true;
  } else {
    LOG_ERROR("get window title failed: {}", ::GetLastError());
  }

  if (!has_title && !(param->external_flags & TRAA_SCREEN_SOURCE_FLAG_NOT_IGNORE_UNTITLED)) {
    return TRUE;
  }

  bool has_process_path = false;
  WCHAR process_path[TRAA_MAX_DEVICE_NAME_LENGTH] = L"";
  if (get_window_process_path(window, process_path, TRAA_MAX_DEVICE_NAME_LENGTH - 1) > 0) {
    has_process_path = true;
  } else {
    LOG_ERROR("get window process path failed: {}", ::GetLastError());
  }

  if ((param->external_flags & TRAA_SCREEN_SOURCE_FLAG_IGNORE_NOPROCESS_PATH) &&
      !has_process_path) {
    return TRUE;
  }

  WCHAR class_name[TRAA_MAX_DEVICE_NAME_LENGTH] = L"";
  const int class_name_length = ::GetClassNameW(window, class_name, TRAA_MAX_DEVICE_NAME_LENGTH);
  if (class_name_length < 1)
    return TRUE;

  if (!(param->external_flags & TRAA_SCREEN_SOURCE_FLAG_NOT_SKIP_SYSTEM_WINDOWS)) {
    if (wcscmp(class_name, L"Progman") == 0 || wcscmp(class_name, L"Program Manager") == 0)
      return TRUE;

    if (wcscmp(class_name, L"TaskManagerWindow") == 0)
      return TRUE;

    if (wcscmp(class_name, L"Button") == 0)
      return TRUE;

    if (wcscmp(class_name, L"Windows.Internal.Shell.TabProxyWindow") == 0)
      return TRUE;
  }

  traa_screen_source_info window_info;
  window_info.id = reinterpret_cast<int64_t>(window);
  window_info.screen_id = get_window_owned_screen_id(window);
  window_info.is_window = true;
  window_info.is_minimized = ::IsIconic(window);

  if (is_window_maximized(window, &window_info.is_maximized) && window_info.is_maximized) {
    get_window_maximized_rect(window, &window_rect);
  }
  window_info.rect = window_rect.to_traa_rect();
  window_info.icon_size = param->icon_size;
  window_info.thumbnail_size = param->thumbnail_size;

  if (has_title) {
    auto utf8_title = string_trans::unicode_to_utf8(window_title);
    strncpy_s(const_cast<char *>(window_info.title), sizeof(window_info.title) - 1,
              utf8_title.c_str(), utf8_title.length());
  }

  if (has_process_path) {
    auto utf8_process_path = string_trans::unicode_to_utf8(process_path);
    strncpy_s(const_cast<char *>(window_info.process_path), sizeof(window_info.process_path) - 1,
              utf8_process_path.c_str(), utf8_process_path.length());
  }

  if (has_process_path && param->icon_size.width > 0 && param->icon_size.height > 0) {
    if (get_process_icon_data(
            process_path, desktop_size(param->icon_size.width, param->icon_size.height),
            const_cast<uint8_t **>(&window_info.icon_data), window_info.icon_size)) {
    } else {
      LOG_ERROR("get icon data failed");
    }
  }

  if (param->thumbnail_size.width > 0 && param->thumbnail_size.height > 0 &&
      param->thumbnail_instance) {
    if (!param->thumbnail_instance->get_thumbnail_data(
            window, param->thumbnail_size, const_cast<uint8_t **>(&window_info.thumbnail_data),
            window_info.thumbnail_size)) {
      LOG_ERROR("get thumbnail data failed");
    }
  }

  param->infos.push_back(window_info);

  return TRUE;
}

4. 枚举窗口信息

最后,我们需要一个函数来枚举所有窗口的信息。

int screen_source_info_enumerator::enum_screen_source_info(const traa_size icon_size,
                                                           const traa_size thumbnail_size,
                                                           const unsigned int external_flags,
                                                           traa_screen_source_info **infos,
                                                           int *count) {
  std::unique_ptr<thumbnail> thumbnail_instance;
  if (thumbnail_size.width > 0 && thumbnail_size.height > 0) {
    thumbnail_instance.reset(new thumbnail());
  }

  enumerator_param param = {
      icon_size, thumbnail_size, external_flags, {}, thumbnail_instance.get()};

  BOOL ret = ::EnumWindows(enum_screen_source_info_proc, reinterpret_cast<LPARAM>(&param));
  if (!ret) {
    LOG_ERROR("call ::EnumWindows failed: {}", ::GetLastError());
    return traa_error::TRAA_ERROR_ENUM_SCREEN_SOURCE_INFO_FAILED;
  }

  *count = static_cast<int>(param.infos.size());
  *infos =
      reinterpret_cast<traa_screen_source_info *>(new traa_screen_source_info[param.infos.size()]);
  if (*infos == nullptr) {
    LOG_ERROR("alloca memroy for infos failed: {}", ::GetLastError());
    return traa_error::TRAA_ERROR_OUT_OF_MEMORY;
  }

  for (size_t i = 0; i < param.infos.size(); ++i) {
    auto &source_info = param.infos[i];
    auto &dest_info = (*infos)[i];
    memcpy(&dest_info, &source_info, sizeof(traa_screen_source_info));
    strncpy_s(const_cast<char *>(dest_info.title), sizeof(dest_info.title) - 1, source_info.title,
              std::strlen(source_info.title));
    strncpy_s(const_cast<char *>(dest_info.process_path), sizeof(dest_info.process_path) - 1,
              source_info.process_path, std::strlen(source_info.process_path));
  }

  return traa_error::TRAA_ERROR_NONE;
}

总结

通过上述步骤,我们可以在Windows系统中获取可进行屏幕共享捕获的窗口列表,并获取它们的图标和缩略图。这一过程涉及到Windows API的使用、窗口属性的获取、图标和缩略图的处理等多个方面。希望本文能对您有所帮助。

最近有点懒了,这还是copilot生成的。。。

源码传送

traa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2223364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

repo将每个仓库回退到第一个commit的状态

文章目录 1. 获取所有仓库的列表2. 回退每个仓库到第一个 commit3. 确认状态注意事项 在使用 repo 和 git 管理 AOSP 代码时&#xff0c;如果你想将每个仓库都回退到其第一个 commit 的状态&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 获取所有仓库的列表 首先&…

Windows Python安装和配置教程

文章目录 一&#xff0c;下载方式&#xff08;一&#xff09;官网下载注&#xff1a;下载选项说明注&#xff1a;查看本机操作系统位数步骤 &#xff08;二&#xff09;网盘下载 二&#xff0c;安装三&#xff0c;测试安装效果&#xff08;一&#xff09;检测安装配置&#xff…

新书速览|Spring+Spring MVC+MyBatis从零开始学(视频教学版)(第3版)

《SpringSpring MVCMyBatis从零开始学&#xff08;视频教学版&#xff09;&#xff08;第3版&#xff09;》 1 本书内容 SSM是当前使用广泛的Java Web开发框架。《SpringSpring MVCMyBatis从零开始学&#xff08;视频教学版&#xff09;&#xff08;第3版&#xff09;》由浅入…

【福建医科大学附属第一医院-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

浅谈BIM+GIS在管廊机电监控与运维管控系统中的应用

文&#xff1a;安科瑞郑桐 摘要&#xff1a;本文提出了一种城市综合管廊监控与报警集成管控平台的架构&#xff0c;并对以BIMGIS为核心的系统功能进行了研究和分析&#xff0c;通过GIS实现对管廊整体及设备设施的全局定位及管理&#xff0c;利用BIM实现对管廊设备的空间定位&a…

第6篇:无线与移动网络

目录 引言 6.1 无线网络的基础概念 6.2 无线局域网&#xff08;WLAN&#xff09;与IEEE 802.11 6.3 蓝牙与无线个域网&#xff08;WPAN&#xff09; 6.4 无线城域网&#xff08;WMAN&#xff09;与WiMax 6.5 ZigBee与智能家居 6.6 移动蜂窝网络&#xff08;3G/4G/5G&…

SpringColoud GateWay 核心组件

优质博文&#xff1a;IT-BLOG-CN 【1】Route路由&#xff1a; Gateway的基本构建模块&#xff0c;它由ID、目标URL、断言集合和过滤器集合组成。如果聚合断言结果为真&#xff0c;则匹配到该路由。 Route路由-动态路由实现原理&#xff1a; 配置变化Apollo 服务地址实例变化…

AMD XILINX 20nm器件价格上调25%

随着市场回暖&#xff0c;台积电也在调整价格策略&#xff0c;近期台积电上调了20nm的出厂价格。 据相关消息显示&#xff0c;AMD为了保障持续的供货和服务&#xff0c;也计划将20nm器件的价格统一上调25%&#xff0c;预计将于11月发布正式的涨价通知&#xff0c;并于2025年Q1开…

七,Linux基础环境搭建(CentOS7)- 安装Scala和Spark

Linux基础环境搭建&#xff08;CentOS7&#xff09;- 安装Scala和Spark 大家注意以下的环境搭建版本号&#xff0c;如果版本不匹配有可能出现问题&#xff01; 一、Scala下载及安装 Scala是一门多范式的编程语言&#xff0c;一种类似java的编程语言&#xff0c;设计初衷是实现…

基于Java+SpringBoot+Vue的宠物咖啡馆平台的设计与实现

基于JavaSpringBootVue的宠物咖啡馆平台的设计与实现 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#…

ubuntu编译kaldi和vosk

文章目录 前言一、开源框架的选取二、kaldi编译三、编译vosk方案一方案二 前言 由于工作需要语音识别的功能&#xff0c;环境是在linux arm版上&#xff0c;所以想先在ubuntu上跑起来看一看&#xff0c;就找了一下语音识别的开源框架&#xff0c;选中了vosk这个开源库&#xf…

微软:全球每天网络攻击超6亿次

《2024年微软数字防御报告》揭示了一个复杂的全球网络安全格局&#xff0c;每天发生超过6亿次网络攻击。报告强调了勒索软件、网络钓鱼和身份泄露事件的增加&#xff0c;以及网络犯罪团伙和国家行为者之间的合作。它强调了人工智能在攻击和防御中的重要作用&#xff0c;敦促组织…

基于SpringBoot的“高校校园点餐系统”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“高校校园点餐系统”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 前台首页功能界面图 用户注册、登录界面图 我…

Java中的程序、进程、线程、并行和并发

程序 一段静态的代码进程 ① 正在内存中运行的“程序” ② 是操作系统调度和分配资源的最小单位线程 ① 进程可进一步细化为线程&#xff0c;是程序内部的一条执行路径&#xff0c;一个进程中至少有一个线程 ② 线程是CPU调度和执行的最小单位 ③ 多个线程共享相同的内存单元&a…

用docker Desktop 下载使用thingsboard/tb-gateway

1、因为正常的docker pull thingsboard/tb-gateway 国内不行了&#xff0c;所以需要其它工具来下载 2、在win下用powershell管理员下运行 docker search thingsboard/tb-gateway 可以访问到了 docker pull thingsboard/tb-gateway就可以下载了 3、docker Desktop就可以看到…

EasyExcel_动态表头的导入导出

文章目录 前言一、EasyExcel二、使用步骤1.引入jar包2.数据准备2.1 数据库 3.方法实例3.1 无实体的导入3.1.1 Controller3.1.2 Service3.1.3 Listener3.1.4 Utils3.1.5 无实体导入数据返回说明 3.2 无实体的导出3.2.1 无实体导出数据(这里只贴出关键代码,Service代码处理)3.2.2…

凸优化学习

认为学习凸优化理论比较合适的路径是&#xff1a; 学习/复习线性代数和&#xff08;少量&#xff09;高等数学的知识。 实际上&#xff0c;凸优化理论综合使用了线性代数和微积分的相关知识&#xff0c;比如方向导数&#xff0c;雅克比矩阵&#xff0c;海森矩阵&#xff0c;KKT…

大数据-189 Elasticsearch - ELK 日志分析实战 - 环境配置启动 Nginx、ZK、Kafka、ES、Kibana

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

写出Windows操作系统内核的程序员,70多岁,还去办公室敲代码

大家好&#xff0c;我是二哥呀。 微软前 CEO 鲍尔默曾说过一句言简意赅的话&#xff1a;“没有 Dave&#xff0c;就没有今天的微软”。 可见 Dave 在微软的分量。Dave 的全名叫 Dave Cutler&#xff0c;微软的超级程序员&#xff0c;Windows NT 操作系统内核的缔造者&#xf…

【Java】ArrayList相关操作及其案例

ArrayList相当于集合&#xff0c;作为一种容器存储数据&#xff0c;与数组类似。不同的是&#xff0c;ArrayList中长度可变&#xff0c;而数组长度不可变。 ArrayList相关API 构造器 public ArrayList() 创建一个空的集合对象 ArrayList<String>arrnew ArrayList<>…