我正在转储客户端窗口的屏幕,并使用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
在那时已经是黑色的。)
答案 0 :(得分:0)
Remy Lebeau
评论说,PrintWindow()
似乎并不可靠,因为它直接捕获了窗口的客户端部分,即使它在屏幕上不可见(例如,它在另一个窗口的后面) )。似乎更好用的一件事是执行常规屏幕截图并将区域限制为目标窗口的客户区域。这样做的缺点是必须可见窗口才能被捕获。为此的代码例如以下:
// Get the horizontal and vertical screen sizes in pixel
inline POINT get_window_resolution(const HWND window_handle)
{
RECT rectangle;
GetClientRect(window_handle, &rectangle);
const POINT coordinates{rectangle.right, rectangle.bottom};
return coordinates;
}
#include <iostream>
#include <ole2.h>
#include <olectl.h>
inline bool save_bitmap(const LPCSTR file_path,
const HBITMAP bitmap, const HPALETTE palette)
{
PICTDESC pict_description;
pict_description.cbSizeofstruct = sizeof(PICTDESC);
pict_description.picType = PICTYPE_BITMAP;
pict_description.bmp.hbitmap = bitmap;
pict_description.bmp.hpal = palette;
LPPICTURE picture;
auto initial_result = OleCreatePictureIndirect(&pict_description, IID_IPicture, false,
reinterpret_cast<void**>(&picture));
if (!SUCCEEDED(initial_result))
{
return false;
}
LPSTREAM stream;
initial_result = CreateStreamOnHGlobal(nullptr, true, &stream);
if (!SUCCEEDED(initial_result))
{
picture->Release();
return false;
}
LONG bytes_streamed;
initial_result = picture->SaveAsFile(stream, true, &bytes_streamed);
const auto file = CreateFile(file_path, GENERIC_WRITE, FILE_SHARE_READ, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (!SUCCEEDED(initial_result) || !file)
{
stream->Release();
picture->Release();
return false;
}
HGLOBAL mem = nullptr;
GetHGlobalFromStream(stream, &mem);
const auto data = GlobalLock(mem);
DWORD bytes_written;
auto result = WriteFile(file, data, bytes_streamed, &bytes_written, nullptr);
result &= bytes_written == static_cast<DWORD>(bytes_streamed);
GlobalUnlock(mem);
CloseHandle(file);
stream->Release();
picture->Release();
return result;
}
inline POINT get_client_window_position(const HWND window_handle)
{
RECT rectangle;
GetClientRect(window_handle, static_cast<LPRECT>(&rectangle));
MapWindowPoints(window_handle, nullptr, reinterpret_cast<LPPOINT>(& rectangle), 2);
const POINT coordinates = {rectangle.left, rectangle.top};
return coordinates;
}
// https://stackoverflow.com/a/9525788/3764804
inline bool capture_screen_client_window(const HWND window_handle, const LPCSTR file_path)
{
SetActiveWindow(window_handle);
const auto hdc_source = GetDC(nullptr);
const auto hdc_memory = CreateCompatibleDC(hdc_source);
const auto window_resolution = get_window_resolution(window_handle);
const auto width = window_resolution.x;
const auto height = window_resolution.y;
const auto client_window_position = get_client_window_position(window_handle);
auto h_bitmap = CreateCompatibleBitmap(hdc_source, width, height);
const auto h_bitmap_old = static_cast<HBITMAP>(SelectObject(hdc_memory, h_bitmap));
BitBlt(hdc_memory, 0, 0, width, height, hdc_source, client_window_position.x, client_window_position.y, SRCCOPY);
h_bitmap = static_cast<HBITMAP>(SelectObject(hdc_memory, h_bitmap_old));
DeleteDC(hdc_source);
DeleteDC(hdc_memory);
const HPALETTE h_palette = nullptr;
if (save_bitmap(file_path, h_bitmap, h_palette))
{
return true;
}
return false;
}
它基于this答案。