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

embedded/2024/10/19 4:31:03/

在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.ppmy.cn/embedded/128648.html

相关文章

NAT机制

目录 1、NAT机制的定义 2、NAT机制的工作原理 1、NAT机制的定义 如今IP地址的分配已经不够使用&#xff0c;为了解决这一问题&#xff0c;NAT机制起到了很关键的作用。 NAT机制&#xff08;网络地址转换&#xff09;&#xff0c;本质上&#xff0c;让一个IP地址代表一批设备…

【工具】VSCODE下载,配置初次设置

打开 settings.json 文件&#xff0c;包含了 Visual Studio Code (VSCode) 中的各种用户配置。 {"files.associations": {"*.vue": "vue","*.wpy": "vue","*.wxml": "html","*.wxss": "…

C语言笔记20

指针运算 #include <stdio.h>int main() {char ac[] {0,1,2,3,4,5,6,7,8,9,};char *p ac;printf("p %p\n", p);printf("p1%p\n", p1);int ai[] {0,1,2,3,4,5,6,7,8,9,};int *q ai;printf("q %p\n", q);printf("q1%p\n", q1)…

状态模式(C++)

定义&#xff1a;状态模式&#xff08;State Pattern&#xff09;是一种行为设计模式&#xff0c;它允许对象在内部状态改变时改变它的行为&#xff0c;对象看起来似乎修改了它的类。状态模式将状态相关的行为封装到单独的类中&#xff0c;并将这些对象组合成状态模式&#xff…

Nginx:proxy_pass指令

proxy_pass 指令在 Nginx 中是实现反向代理和负载均衡的重要指令。 一. 反向代理 在反向代理的场景下&#xff0c;proxy_pass 指令用于将接收到的请求转发给另一个后端服务器。后端服务器地址可以是 IP 地址加端口、域名加端口、或者一个完整的 URL。 注意事项 proxy_pass …

Python近红外光谱数据分析技术

原文链接&#xff1a;Python近红外光谱数据分析技术https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247618985&idx4&sncb10b44822eeb8e30eadd9e45fbdcdc4&chksmfa82504ecdf5d9584a78de0adfc518c159baf1b70b26106a621adf1aa0bfaf1c67b7203e4b7c&toke…

K8s(学习笔记)

swap分区是什么呀&#xff1f; 什么是ipvs呀&#xff1f; yaml是什么呀&#xff1f;&#xff1f;&#xff1f; p20看不下去了&#xff01;&#xff01;&#xff01;

Springboot集成Kafka

一、添加依赖 我们使用spring本身支持的spring-kafka依赖&#xff0c;但是需要注意版本问题&#xff0c;不同的springboot版本支持不同的kafka版本&#xff0c;避免因版本不同带来困扰&#xff01;参考下图&#xff1a; 或者访问官网查看版本对应关系&#xff1a;Spring for Ap…