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

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

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

前置知识

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

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

实现步骤

1. 包含必要的头文件

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

cpp 复制代码
#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. 定义辅助函数

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

获取窗口属性
cpp 复制代码
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;
}
获取窗口文本
cpp 复制代码
int get_window_text_safe(HWND window, LPWSTR p_string, int cch_max_count) {
  return ::InternalGetWindowText(window, p_string, cch_max_count);
}
获取进程路径
cpp 复制代码
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. 定义窗口枚举回调函数

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

cpp 复制代码
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. 枚举窗口信息

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

cpp 复制代码
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

相关推荐
cpsvps_net10 小时前
美国服务器环境下Windows容器工作负载智能弹性伸缩
windows
甄超锋10 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
cpsvps12 小时前
美国服务器环境下Windows容器工作负载基于指标的自动扩缩
windows
胡耀超14 小时前
DataOceanAI Dolphin(ffmpeg音频转化教程) 多语言(中国方言)语音识别系统部署与应用指南
python·深度学习·ffmpeg·音视频·语音识别·多模态·asr
byxdaz14 小时前
FFmpeg QoS 处理
ffmpeg
网硕互联的小客服15 小时前
Apache 如何支持SHTML(SSI)的配置方法
运维·服务器·网络·windows·php
etcix15 小时前
implement copy file content to clipboard on Windows
windows·stm32·单片机
许泽宇的技术分享16 小时前
Windows MCP.Net:基于.NET的Windows桌面自动化MCP服务器深度解析
windows·自动化·.net
非凡ghost17 小时前
AMS PhotoMaster:全方位提升你的照片编辑体验
windows·学习·信息可视化·软件需求
mortimer18 小时前
一次与“顽固”外部程序的艰难交锋:subprocess 调用exe踩坑实录
windows·python·ai编程