I'm dumping the screen of a client window and writing it to the disk using C++
and the Windows API
as follows:
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;
}
On my machine (Windows 10 Pro 64-bit
) this code always works perfectly. As you can see, the return values are validated so errors should be caught. However, another user on Windows 10 64-bit
always gets a black screen image despite all the functions succeeding. Is there anything I'm not addressing correctly? How can this bug be fixed? I've tried using PNG
and BMP
encoders but same outcome (most likely because the HBITMAP
was already black at that point).