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

相关推荐
吴声子夜歌4 小时前
Java数据结构与算法——基本数学问题
java·开发语言·windows
这儿有一堆花7 小时前
用原生脚本编写无害恶作剧
windows
因我你好久不见7 小时前
Windows部署springboot jar支持开机自启动
windows·spring boot·jar
夜流冰7 小时前
Excel - MS Support for Excel: 2 Collaborate
数据库·windows·excel
林瞅瞅8 小时前
PowerShell 启动卡顿?内存飙升?原来是 800MB 的历史记录在作祟!
windows
Shepherd06199 小时前
【Windows Server 实战】WAC 反向代理配置
windows
云小逸9 小时前
【windows系统编程】第一章 Windows 系统核心架构与基础概念
windows·架构
怣疯knight10 小时前
Docker Desktop 4.55.0版本安装成功教程
windows·docker
心动啊12110 小时前
FFMPeg在Python中的使用
ffmpeg
aqi0011 小时前
FFmpeg开发笔记(一百)国产的Android开源视频压缩工具VideoSlimmer
android·ffmpeg·音视频·直播·流媒体