skip to Main Content

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


  1. According to When to Draw in a Window, It’s practical to paint outside of WM_PAINT.

    If an application draws at any other time, such as from within WinMain
    or during the processing of keyboard or mouse messages, it calls the
    GetDC or GetDCEx function to retrieve the display DC.

    As @IgorTandetnik explained yesterday, probably due to the painting happens before ShowWindow, It’s erased because of ShowWindow drawing with default white background. The following code works for me.

        // Display window
        ShowWindow(hwnd, nCmdShow);
    
        // Show screenshot in window
        HDC hdc = GetDC(hwnd);
        Graphics graphics(hdc);
        graphics.DrawImage(&image, 0, 0, width, height);
        ReleaseDC(hwnd, hdc);
    
    Login or Signup to reply.
  2. 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), and WM_DESTROY. The latter is required so that we can break out of the following message loop by posting a WM_QUIT thread message.

    The following implementation also handles WM_CREATE to enable passing the bitmap handle into CreateWindowEx and storing it in per-window memory for convenient access.

    ScreenshotWndProc:

    LRESULT CALLBACK ScreenshotWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        switch (Msg)
        {
        case WM_CREATE: {
            // Read `HBITMAP` from `CREATESTRUCT`
            auto const params = reinterpret_cast<CREATESTRUCT const*>(lParam)->lpCreateParams;
            auto const bitmap = reinterpret_cast<HBITMAP>(params);
            // Store the bitmap at index 0
            ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(bitmap));
            // Continue window creation
            return 0;
        }
    
        case WM_DESTROY:
            ::PostQuitMessage(0);
            break;
    
        case WM_PAINT: {
            PAINTSTRUCT ps = {};
            HDC hdc = ::BeginPaint(hWnd, &ps);
    
            // Get client size
            RECT rc = {};
            ::GetClientRect(hWnd, &rc);
            auto const width = rc.right - rc.left;
            auto const height = rc.bottom - rc.top;
    
            // Retrieve bitmap from window
            auto const bitmap = reinterpret_cast<HBITMAP const>(::GetWindowLongPtr(hWnd, 0));
    
            // Create a memory device context to blit from
            auto const src_dc = ::CreateCompatibleDC(hdc);
            auto const prev_bmp = ::SelectObject(src_dc, bitmap);
    
            // Blit bitmap to client area
            ::BitBlt(hdc, 0, 0, width, height, src_dc, 0, 0, SRCCOPY);
    
            // Clean up memory device context
            ::SelectObject(src_dc, prev_bmp);
            ::DeleteDC(src_dc);
    
            ::EndPaint(hWnd, &ps);
    
            // Message has been processed
            return 0;
        }
    
        default:
            break;
        }
    
        return ::DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    

    Adjusted window class registration and window creation:

        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 = ScreenshotWndProc;
        wc.cbWndExtra = sizeof(HBITMAP); // reserve memory for one `HBITMAP`
        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, bitmap);
    

    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:

    #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;
    }
    
    LRESULT CALLBACK ScreenshotWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
    {
        switch (Msg)
        {
        case WM_CREATE: {
            // Read `HBITMAP` from `CREATESTRUCT`
            auto const params = reinterpret_cast<CREATESTRUCT const*>(lParam)->lpCreateParams;
            auto const bitmap = reinterpret_cast<HBITMAP>(params);
            // Store the bitmap at index 0
            ::SetWindowLongPtr(hWnd, 0, reinterpret_cast<LONG_PTR>(bitmap));
            // Continue window creation
            return 0;
        }
    
        case WM_DESTROY:
            ::PostQuitMessage(0);
            break;
    
        case WM_PAINT: {
            PAINTSTRUCT ps = {};
            HDC hdc = ::BeginPaint(hWnd, &ps);
    
            // Get client size
            RECT rc = {};
            ::GetClientRect(hWnd, &rc);
            auto const width = rc.right - rc.left;
            auto const height = rc.bottom - rc.top;
    
            // Retrieve bitmap from window
            auto const bitmap = reinterpret_cast<HBITMAP const>(::GetWindowLongPtr(hWnd, 0));
    
            // Create a memory device context to blit from
            auto const src_dc = ::CreateCompatibleDC(hdc);
            auto const prev_bmp = ::SelectObject(src_dc, bitmap);
    
            // Blit bitmap to client area
            ::BitBlt(hdc, 0, 0, width, height, src_dc, 0, 0, SRCCOPY);
    
            // Clean up memory device context
            ::SelectObject(src_dc, prev_bmp);
            ::DeleteDC(src_dc);
    
            ::EndPaint(hWnd, &ps);
    
            // Message has been processed
            return 0;
        }
    
        default:
            break;
        }
    
        return ::DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    
    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;
        }
    
        // Select `bitmap` out of memory device context
        SelectObject(mem_dc, old_bitmap);
        // We're done with the DC's, so clean them up
        DeleteDC(mem_dc);
        ReleaseDC(NULL, screen_dc);
    
        // 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 = ScreenshotWndProc;
        wc.cbWndExtra = sizeof(HBITMAP); // reserve memory for one `HBITMAP`
        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, bitmap);
    
        if (hwnd == NULL)
        {
            printf("Error: CreateWindowEx failed to create windown");
            return;
        }
    
        // 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);
        }
    
        // Make sure the `bitmap` lives as long the window
        DeleteObject(bitmap);
    
        // Clean up
        UnregisterClass(L"ScreenshotWindowClass", hInstance);
    }
    
    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;
    }
    

    As an aside: The nWidth and nHeight arguments passed into CreateWindowEx designate the window size. The code suggests that what you actually want is a window with a desired client size. You can call AdjustWindowRectEx to calculate the window rectangle that corresponds to a given client rectangle.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search