I made a program that takes a screenshot and saves it in the local folder (this worked). I deleted that part and wanted to show the newly taken screenshot in a newly created window. Everything is good, the screenshot gets taken, the window gets created but the only problem is that the window does not show the taken screenshot.
If you want to run the code in Visual Studio you have to change Properties -> Linker -> System -> SubSystem (in Visual Studio)
from Windows (/SUBSYSTEM:WINDOWS)
to Console (/SUBSYSTEM:CONSOLE)
How can I show the newly taken screenshot in the newly created window?
This is the code:
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <objidl.h>
#include <stdio.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0;
UINT size = 0;
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0) {
printf("Error: GetImageEncodersSize returned size 0n");
return -1;
}
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL) {
printf("Error: malloc failed to allocate memory for ImageCodecInfon");
return -1;
}
if (GetImageEncoders(num, size, pImageCodecInfo) != Ok) {
printf("Error: GetImageEncoders failedn");
free(pImageCodecInfo);
return -1;
}
for (UINT j = 0; j < num; ++j) {
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j;
}
}
free(pImageCodecInfo);
return -1;
}
void TakeScreenshot()
{
// Get the dimensions of the whole desktop
int dpi_x = GetDpiForSystem();
int dpi_y = GetDpiForSystem();
int width = GetSystemMetricsForDpi(SM_CXSCREEN, dpi_x);
int height = GetSystemMetricsForDpi(SM_CYSCREEN, dpi_y);
if (width == 0 || height == 0) {
printf("Error: GetSystemMetrics returned invalid screen dimensionsn");
return;
}
// Create a bitmap to hold the screenshot
HDC screen_dc = GetDC(NULL);
if (screen_dc == NULL) {
printf("Error: GetDC failed to get a handle to the screen device contextn");
return;
}
HDC mem_dc = CreateCompatibleDC(screen_dc);
if (mem_dc == NULL) {
printf("Error: CreateCompatibleDC failed to create a compatible device contextn");
ReleaseDC(NULL, screen_dc);
return;
}
// Create a bitmap that is scaled to the appropriate DPI
HBITMAP bitmap = CreateBitmap(width, height, 1, GetDeviceCaps(screen_dc, BITSPIXEL), NULL);
if (bitmap == NULL) {
printf("Error: CreateCompatibleBitmap failed to create a compatible bitmapn");
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
return;
}
HGDIOBJ old_bitmap = SelectObject(mem_dc, bitmap);
// Set the DPI of the memory DC to match the system DPI
SetGraphicsMode(mem_dc, GM_ADVANCED);
XFORM xform;
xform.eM11 = (FLOAT)dpi_x / 96;
xform.eM12 = xform.eM21 = xform.eM22 = 0;
xform.eDx = xform.eDy = 0;
SetWorldTransform(mem_dc, &xform);
// Copy the screen contents to the bitmap
if (BitBlt(mem_dc, 0, 0, width, height, screen_dc, 0, 0, SRCCOPY) == 0) {
printf("Error:BitBlt failed to copy screen contents to bitmapn");
return;
}
// Save the bitmap to a file
Gdiplus::Bitmap image(bitmap, NULL);
if (image.GetLastStatus() != Ok) {
printf("Error: Bitmap constructor failed to create a Bitmap objectn");
return;
}
CLSID png_clsid;
int r = GetEncoderClsid(L"image/png", &png_clsid);
if (r == -1)
{
printf("Error: unable to find image encoder for MIME type 'image/png'n");
return;
}
HWND hwnd;
WNDCLASSEX wc;
HINSTANCE hInstance = GetModuleHandle(NULL);
// Define window class
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DefWindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"ScreenshotWindowClass";
RegisterClassEx(&wc);
// Create window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
L"ScreenshotWindowClass",
L"Screenshot",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, width, height,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
printf("Error: CreateWindowEx failed to create windown");
return;
}
// Show screenshot in window
HDC hdc = GetDC(hwnd);
Graphics graphics(hdc);
graphics.DrawImage(&image, 0, 0, width, height);
ReleaseDC(hwnd, hdc);
// Display window
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
// Wait for user to close window
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Clean up
UnregisterClass(L"ScreenshotWindowClass", hInstance);
// Clean up
SelectObject(mem_dc, old_bitmap);
DeleteObject(bitmap);
DeleteDC(mem_dc);
ReleaseDC(NULL, screen_dc);
}
int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
DPI_AWARENESS_CONTEXT dpi_awareness_context = SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);
TakeScreenshot();
SetThreadDpiAwarenessContext(dpi_awareness_context);
GdiplusShutdown(gdiplusToken);
return 0;
}
2
Answers
According to When to Draw in a Window, It’s practical to paint outside of WM_PAINT.
As @IgorTandetnik explained yesterday, probably due to the painting happens before
ShowWindow
, It’s erased because ofShowWindow
drawing with default white background. The following code works for me.The core issue here is the attempt to render onto a window "from the outside". Displaying window contents is difficult to do without the target window’s cooperation. What you should do instead is have the window itself handle the display.
When registering a window class, the function pointer assigned to
lpfnWndProc
is what implements the window’s behavior. In a simple case like this there are only two messages that need to be handled explicitly:WM_PAINT
(responsible for rendering the client area), andWM_DESTROY
. The latter is required so that we can break out of the following message loop by posting aWM_QUIT
thread message.The following implementation also handles
WM_CREATE
to enable passing the bitmap handle intoCreateWindowEx
and storing it in per-window memory for convenient access.ScreenshotWndProc
:Adjusted window class registration and window creation:
Since this design relies on the intermediate window holding a reference to the screenshot bitmap, it is important for that bitmap to no longer be selected into any device context, and also outlive the window. This requires re-arranging part of the resource management code, so here is the full sample code for reference:
As an aside: The
nWidth
andnHeight
arguments passed intoCreateWindowEx
designate the window size. The code suggests that what you actually want is a window with a desired client size. You can callAdjustWindowRectEx
to calculate the window rectangle that corresponds to a given client rectangle.