Click here to Skip to main content
15,880,796 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have created a small demo app which window looks as below:

https://i.stack.imgur.com/iW05q.png[^]

When I run this demo app, and press any key, I want to capture the part of the screen bitmap.

The part of the screen I am interested in, is the one my window occupies, namely the content of the top rectangle in my window that holds letters. The captured screen bitmap should look like below:

https://i.stack.imgur.com/9eSVH.png[^]

The problem I face is that screen capturing code captures wrong part of the screen.

Below is the full code (bare in mind that I tried to keep things as minimal as possible):

C++
#include <Windows.h>

void foo(HWND hWnd)
{
    HDC hdcScreen;
    HDC hdcWindow;

    hdcScreen = GetDC(NULL);
    hdcWindow = GetDC(hWnd);
    
    RECT rcClient;
    GetClientRect(hWnd, &rcClient);

    // map window's client coordinates to screen coordinates
    // HERE IS THE PROBLEM, SOMEHOW COORDINATES ARE NOT TRANSLATED CORRECTLY 
    // do not know how to fix this, but I am trying  :( 
    RECT rc1 = rcClient;
    MapWindowPoints(hWnd, NULL, (LPPOINT)&rc1, 2);  

    // capture desktop portion of the image
    // that corresponds to the window's top rectangle (the one that has letters in it)
    // and blit the result in the bottom rectangle
    // so result can be visually compared
    if (!BitBlt(hdcWindow, 
        rcClient.left + 50, // coordinates of the bottom rectangle 
        rcClient.top + 70,  // sorry for the "magic numbers" 
        75, 35,             // I am low on time :( 
        hdcScreen,           
        rc1.left + 50,      // screen coordinates of the top rectangle     
        rc1.top + 20,       // (the one that contains letters) 
        SRCCOPY))            
    {
        OutputDebugString(L"StretchBlt has failed");
        ReleaseDC(NULL, hdcScreen);
        ReleaseDC(hWnd, hdcWindow);
        return;
    }
    
    RECT rcBottomRect;                        // Frame again the bottom rectangle in the window,  
    rcBottomRect.left = rcClient.left + 50;   // to make visual comparing easier
    rcBottomRect.top = rcClient.top + 70;     // and to verify that I didn't screw up
    rcBottomRect.right = rcClient.left + 125; // the coordinates
    rcBottomRect.bottom = rcClient.top + 105;

    HBRUSH br = (HBRUSH)GetStockObject(BLACK_BRUSH);
    FrameRect(hdcWindow, &rcBottomRect, br);

    ReleaseDC(NULL, hdcScreen);
    ReleaseDC(hWnd, hdcWindow);
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_KEYUP:      // easiest handler to add that keeps things minimal
        foo(hwnd);      // capture screen      
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        RECT rcClient;
        GetClientRect(hwnd, &rcClient);

        RECT rcTopRect;            
        rcTopRect.left = rcClient.left + 50;
        rcTopRect.top = rcClient.top + 20;
        rcTopRect.right = rcTopRect.left + 75;
        rcTopRect.bottom = rcTopRect.top + 35;

        HBRUSH br = (HBRUSH)GetStockObject(BLACK_BRUSH);

        TextOut(hdc, 20, 30, L"Asdf ghj kkl oioio 4545 676767", ARRAYSIZE(L"Asdf ghj kkl oioio 4545 676767"));
        FrameRect(hdc, &rcTopRect, br);

        RECT rcBottomRect;
        rcBottomRect.left = rcClient.left + 50;
        rcBottomRect.top = rcClient.top + 70;
        rcBottomRect.right = rcClient.left + 125;
        rcBottomRect.bottom = rcClient.top + 105;

        FrameRect(hdc, &rcBottomRect, br);

        EndPaint(hwnd, &ps);
    }
    break;
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

// BOILERPLATE CODE...
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = L"myWindowClass";
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"Window Registration Failed!", L"Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        L"myWindowClass",
        L"MVCE",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 300, 170,
        NULL, NULL, hInstance, NULL);

    if (hwnd == NULL)
    {
        MessageBox(NULL, L"Window Creation Failed!", L"Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // Step 3: The Message Loop
    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}


How to fix calculation "error" (maybe it is not an error, maybe I am misusing the API?) with MapWindowPoints?

UPDATE:

I have forgotten to mention that I have 2 monitors. After testing the app on second monitor everything worked fine.

After going through the settings for the first monitor, I have found out that it is set to scale text, apps and other items to 150%.

Reverting it to 100% made the code work, but now I need to find a solution for this case, as I may not force users to change their settings.

Any help would be appreciated.

What I have tried:

Commenting out MapWindowPoints didn't help, ClientToScreen didn't help either, nor have I found alternative API...
Posted
Updated 8-Jul-20 7:11am
v3
Comments
Richard MacCutchan 8-Jul-20 9:10am    
Run the code through the debugger so you can see exactly what values are being used for the co-ordinates.
MyOldAccount 8-Jul-20 9:36am    
Hi Rich! Long time no see :)

I have gone through the debugger, no errors, everything "works" it is just that coordinates are "wrong".

I wonder if I am misusing the API? I really don't know what could be the problem...
Richard MacCutchan 8-Jul-20 9:47am    
Well I have no idea what you mean by "wrong".
Richard MacCutchan 8-Jul-20 9:59am    
I just ran the code to convert the co-ordinates and the answers all look correct.
Luc Pattyn 8-Jul-20 10:05am    
I suggest you run your code on a screen that shows a large (background) image, so you can see which (wrong) part of the screen you are actually capturing; that would make it a lot easier to figure out in what way your coordinate manipulations are failing...

It's capturing exactly what your code is telling it to capture. You have no choice but to run this code under the debugger, examining variables to make sure the values are what they are supposed to be.

The code isn't doing what you think it's doing. The debugger is there to debug YOU and your understanding of the code so you can change your thinking of how the code works and improve it.
 
Share this answer
 
Comments
MyOldAccount 8-Jul-20 11:07am    
Thank you for your suggestion. I think I have found the problem (see the UPDATE on the bottom of the post), now I need to find a solution...
You should respect the scaling how Richard wrote and also deal with multi monitor system. Some years ago I wrote an Repositioning Windows on Multiple Monitor Systems article which deals with that issue.

But nevertheless your foo function looks to be correct and only need to be enhanced in that manners.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900