FFMPEG录屏(19)--- 枚举Windows下的屏幕列表,并获取名称、缩略图

在Windows下枚举显示器列表并获取名称、缩略图

在Windows系统中,枚举显示器列表并获取它们的名称和缩略图是一个常见的需求。本文将详细介绍如何实现这一功能,涉及到的主要技术包括Windows API和C++编程。

获取显示器信息

首先,我们需要一个函数来枚举所有的显示器,并获取它们的名称和缩略图。

cpp 复制代码
int enum_screens(enumerator_param &param) {
  BOOL enum_result = TRUE;
  for (int device_index = 0;; ++device_index) {
    DISPLAY_DEVICEW device;
    device.cb = sizeof(device);
    enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);

    if (!enum_result) {
      break;
    }

    if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
      continue;
    }

    bool is_primary = false;
    if (device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) {
      is_primary = true;
    }

    DEVMODEW device_mode;
    device_mode.dmSize = sizeof(device_mode);
    device_mode.dmDriverExtra = 0;
    BOOL result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
    if (!result) {
      break;
    }

    traa_screen_source_info screen_info;
    screen_info.is_window = false;
    screen_info.id = device_index;
    screen_info.rect = traa_rect(device_mode.dmPosition.x, device_mode.dmPosition.y,
                                 device_mode.dmPelsWidth, device_mode.dmPelsHeight);
    auto utf8_name = string_trans::unicode_to_utf8(device.DeviceName);
    strncpy_s(const_cast<char *>(screen_info.title), sizeof(screen_info.title) - 1,
              utf8_name.c_str(), utf8_name.length());

    if (param.thumbnail_size.width > 0 && param.thumbnail_size.height > 0 &&
        param.thumbnail_instance) {
      capture_utils::get_screen_image_by_gdi(screen_info.rect, param.thumbnail_size,
                                             const_cast<uint8_t **>(&screen_info.thumbnail_data),
                                             screen_info.thumbnail_size);
    }

    param.infos.push_back(screen_info);
  }

  return traa_error::TRAA_ERROR_NONE;
}

这个函数使用EnumDisplayDevicesW来枚举所有的显示器,并使用EnumDisplaySettingsExW来获取每个显示器的设置。然后,我们将显示器的信息存储在traa_screen_source_info结构体中,并将其添加到参数的infos向量中。

获取缩略图

获取显示器的缩略图是一个关键步骤。我们需要使用GDI(图形设备接口)来捕获屏幕图像,并将其缩放到我们需要的大小。

cpp 复制代码
bool capture_utils::get_screen_image_by_gdi(const traa_rect &rect, const traa_size &target_size,
                                            uint8_t **data, traa_size &scaled_size) {
  const desktop_size scaled_desktop_size =
      calc_scaled_size(desktop_size(rect.right - rect.left, rect.bottom - rect.top),
                       desktop_size(target_size.width, target_size.height));
  if (scaled_desktop_size.is_empty()) {
    LOG_ERROR("calc scaled scaled_size failed, get empty scaled_size");
    return false;
  }

  HDC screen_dc = ::GetDC(nullptr);
  if (!screen_dc) {
    LOG_ERROR("get screen dc failed: {}", ::GetLastError());
    return false;
  }

  bool result = false;
  HANDLE section = nullptr;
  uint8_t *bitmap_data = nullptr;
  HBITMAP bitmap = nullptr;
  HDC compatible_dc = nullptr;
  HGDIOBJ old_obj = nullptr;

  do {
    constexpr int bytes_per_pixel = desktop_frame::kBytesPerPixel;

    BITMAPINFO bmi = {};
    bmi.bmiHeader.biWidth = scaled_desktop_size.width();
    bmi.bmiHeader.biHeight = -scaled_desktop_size.height();
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32;
    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
    bmi.bmiHeader.biSizeImage =
        scaled_desktop_size.width() * scaled_desktop_size.height() * bytes_per_pixel;

    bitmap = ::CreateDIBSection(screen_dc, &bmi, DIB_RGB_COLORS, (void **)&bitmap_data, section, 0);
    if (!bitmap) {
      LOG_ERROR("create dib section failed: {}", ::GetLastError());
      break;
    }

    compatible_dc = ::CreateCompatibleDC(screen_dc);
    old_obj = ::SelectObject(compatible_dc, bitmap);
    if (!old_obj || old_obj == HGDI_ERROR) {
      LOG_ERROR("select object failed: {}", ::GetLastError());
      break;
    }

    SetStretchBltMode(compatible_dc, COLORONCOLOR);
    result = ::StretchBlt(compatible_dc, 0, 0, scaled_desktop_size.width(),
                          scaled_desktop_size.height(), screen_dc, rect.left, rect.top,
                          rect.right - rect.left, rect.bottom - rect.top, SRCCOPY | CAPTUREBLT);
    if (!result) {
      LOG_ERROR("stretch blt failed: {}", ::GetLastError());
      break;
    }

    *data = new uint8_t[bmi.bmiHeader.biSizeImage];
    if (!*data) {
      LOG_ERROR("alloc memory for thumbnail data failed: {}", ::GetLastError());
      break;
    }

    memcpy_s(*data, bmi.bmiHeader.biSizeImage, bitmap_data, bmi.bmiHeader.biSizeImage);

    scaled_size = scaled_desktop_size.to_traa_size();
  } while (0);

  if (bitmap) {
    ::DeleteObject(bitmap);
  }

  if (compatible_dc) {
    if (old_obj) {
      ::SelectObject(compatible_dc, old_obj);
    }
    ::DeleteDC(compatible_dc);
  }

  ::ReleaseDC(nullptr, screen_dc);

  if (!result && *data) {
    delete[] * data;
    *data = nullptr;
  }

  return result;
}

这个函数使用CreateDIBSection创建一个设备独立位图(DIB),然后使用StretchBlt将屏幕图像复制到位图中。最后,我们将位图数据复制到一个新的缓冲区中,并返回缩放后的大小。

整合一切

最后,我们需要一个函数来整合所有的步骤,枚举显示器并获取它们的名称和缩略图。

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()};

  enum_windows(param);
  enum_screens(param);

  *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));
    if (std::strlen(source_info.title) > 0) {
      strncpy_s(const_cast<char *>(dest_info.title), sizeof(dest_info.title) - 1, source_info.title,
                std::strlen(source_info.title));
    }

    if (std::strlen(source_info.process_path) > 0) {
      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;
}

这个函数创建一个thumbnail实例,并调用enum_windowsenum_screens来枚举窗口和显示器。然后,它将枚举到的信息复制到一个新的缓冲区中,并返回信息的数量。

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

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

源码传送

traa

相关推荐
狂热开发者30 分钟前
用 Typeoff 把会后口述整理成 Markdown 决策记录
人工智能·windows·macos·ios·安卓
ofoxcoding1 小时前
Codex Computer Use 完整指南:AI 自动操控 Mac 与 Windows 桌面实战详解
人工智能·windows·macos·ai
XUHUOJUN2 小时前
Windows Server 2025 Stretch Cluster 架构详解
windows·microsoft·azure local
zhou135284822673 小时前
Windows 配置 WSL (Ubuntu) 环境完整指南
linux·windows·ubuntu
XUHUOJUN3 小时前
Windows Server 2025 RAC vs Stretch Cluster 技术定位
windows·microsoft·azure local
sg_knight5 小时前
Claude Code 安装指南(macOS / Linux / Windows WSL)
linux·windows·macos·claude·code·anthropic·claude-code
caimouse20 小时前
Windows Research Kernel (WRK) 内存管理器完整分析
windows
济*沧*海20 小时前
BaseMapper的介绍
java·windows·tomcat
vortex520 小时前
PowerShell Provider 机制详解:从文件系统到注册表的统一抽象
windows·powershell
小狮子&1 天前
ubuntu2604终端右键新建窗口
windows