在Windows下枚举显示器列表并获取名称、缩略图
在Windows系统中,枚举显示器列表并获取它们的名称和缩略图是一个常见的需求。本文将详细介绍如何实现这一功能,涉及到的主要技术包括Windows API和C++编程。
获取显示器信息
首先,我们需要一个函数来枚举所有的显示器,并获取它们的名称和缩略图。
cpp
int enum_screens(enumerator_param ¶m) {
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_windows
和enum_screens
来枚举窗口和显示器。然后,它将枚举到的信息复制到一个新的缓冲区中,并返回信息的数量。
通过上述步骤,我们可以在Windows系统中枚举显示器列表,并获取它们的名称和缩略图。这一过程涉及到Windows API的使用、窗口属性的获取、图标和缩略图的处理等多个方面。希望本文能对您有所帮助。
最近有点懒了,这还是copilot生成的。。。