【发布时间】:2019-11-04 17:42:30
【问题描述】:
我正在转储客户端窗口的屏幕并使用C++ 和Windows API 将其写入磁盘,如下所示:
const auto window_handle = FindWindow(nullptr, ...);
const auto output_file_path = L"output.png";
// Get the window screenshot
std::cout << "Performing screenshot... ";
HBITMAP bitmap;
const auto screen_shot_successfully_performed = perform_screen_shot(window_handle, bitmap);
if (screen_shot_successfully_performed)
{
std::cout << "OK!" << std::endl;
// Save it
std::cout << "Saving image... ";
const auto saving_image_succeeded = save_image(bitmap, output_file_path);
if (saving_image_succeeded)
{
std::cout << "OK!" << std::endl;
}
}
ImageEncoding.h:
#pragma once
#include <windows.h>
#include <gdiplus.h>
#include <gdiplustypes.h>
#include <iostream>
#include "ErrorHandling.h"
#include "Conversions.h"
using namespace Gdiplus;
#pragma comment (lib, "Gdiplus.lib")
inline int get_encoder(const WCHAR* format, CLSID* p_clsid)
{
UINT image_encoders_count = 0;
UINT image_encoder_array_size = 0;
GetImageEncodersSize(&image_encoders_count, &image_encoder_array_size);
if (image_encoder_array_size == 0)
{
return -1; // Failure
}
const auto p_image_codec_info = static_cast<ImageCodecInfo*>(malloc(image_encoder_array_size));
if (p_image_codec_info == nullptr)
{
return -1; // Failure
}
GetImageEncoders(image_encoders_count, image_encoder_array_size, p_image_codec_info);
for (UINT image_encoder_index = 0; image_encoder_index < image_encoders_count; image_encoder_index++)
{
const auto image_codec_info = p_image_codec_info[image_encoder_index];
const auto mime_type = image_codec_info.MimeType;
const auto comparison_result = wcscmp(mime_type, format);
if (comparison_result == 0)
{
*p_clsid = image_codec_info.Clsid;
free(p_image_codec_info);
return image_encoder_index; // Success
}
}
free(p_image_codec_info);
return -1; // Failure
}
inline WCHAR* get_image_format_from_filename(const WCHAR* filename)
{
std::wstring wide_string_filename(filename);
const std::string filename_string(wide_string_filename.begin(),
wide_string_filename.end());
const auto file_extension = get_file_extension(filename_string);
std::stringstream image_format_buffer;
image_format_buffer << "image/" << file_extension;
const auto encoder_format = image_format_buffer.str();
return to_wide_char(encoder_format.c_str());
}
inline bool save_image(const HBITMAP bitmap, const WCHAR* filename)
{
GdiplusStartupInput gdiplus_startup_input;
ULONG_PTR gdiplus_token;
const auto startup_status = GdiplusStartup(&gdiplus_token, &gdiplus_startup_input, nullptr);
if (startup_status != Gdiplus::Ok)
{
std::cout << "[ERROR] GdiplusStartup() failed: " << startup_status << std::endl;
return false;
}
auto image = new Bitmap(bitmap, nullptr);
CLSID my_cls_id;
const auto format = get_image_format_from_filename(filename);
const auto encoder_return_value = get_encoder(format, &my_cls_id);
if (encoder_return_value == -1)
{
std::cout << "[ERROR] Encoder not available: ";
std::wcout << format << std::endl;
delete image;
return false;
}
const auto image_saving_status = image->Save(filename, &my_cls_id, nullptr);
if (image_saving_status != Gdiplus::Ok)
{
std::cout << "[ERROR] Saving image failed: " << startup_status << std::endl;
delete image;
return false;
}
delete image;
GdiplusShutdown(gdiplus_token);
return true;
}
inline bool perform_screen_shot(const HWND window_handle, HBITMAP& bitmap)
{
RECT rectangle;
const auto successful = GetClientRect(window_handle, &rectangle);
if (!successful)
{
const auto last_error_message = get_last_error_as_string();
std::cout << "[ERROR] Cannot get client rectangle: " << last_error_message << std::endl;
exit(EXIT_FAILURE);
}
if (IsRectEmpty(&rectangle))
{
std::cout << "[ERROR] The client rectangle is empty: Maybe the window is minimized?" << std::endl;
return false;
}
const auto hdc_screen = GetDC(nullptr);
const auto hdc = CreateCompatibleDC(hdc_screen);
bitmap = CreateCompatibleBitmap(hdc_screen,
rectangle.right - rectangle.left,
rectangle.bottom - rectangle.top);
SelectObject(hdc, bitmap);
const auto window_successfully_printed = PrintWindow(window_handle, hdc, PW_CLIENTONLY);
if (!window_successfully_printed)
{
const auto last_error_message = get_last_error_as_string();
std::cout << "[ERROR] Window not printed: " << last_error_message << std::endl;
exit(EXIT_FAILURE);
}
return true;
}
在我的机器 (Windows 10 Pro 64-bit) 上,这段代码总是能完美运行。如您所见,返回值经过验证,因此应捕获错误。然而,Windows 10 64-bit 上的另一个用户总是得到一个黑屏图像,尽管所有功能都成功了。有什么我没有正确解决的吗?如何修复此错误?我试过使用PNG 和BMP 编码器,但结果相同(很可能是因为HBITMAP 那时已经是黑色的了)。
【问题讨论】:
-
PrintWindow()不太好用。尝试从该 DC 到您的位图中的GetWindowDC()和BitBlt()。或者GetClientRect()+ClientToScreen()和BitBlt()从屏幕 DC 到您的位图的那个矩形。或者查看Win10中添加的Screen Capture API。或者在 Win8 中添加的Desktop Duplication API。或 Direct3D。见Ways to capture the screen -
为避免 UWP 应用 (DWM) 出现黑屏,您必须添加标志 PW_RENDERFULLCONTENT(它在我的 Windows 10 版本(1803、17134.820)上运行良好)
-
@Castorix:使用此标志不会生成客户区屏幕截图。屏幕截图包括标题栏,并在我的机器上向左移动了一点。此外,未记录此标志:docs.microsoft.com/en-us/windows/desktop/api/winuser/…