MyOldAccount Ответов: 3

Захват неправильной части экрана


Я создал небольшое демо-приложение, окно которого выглядит следующим образом:

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

Когда я запускаю это демо-приложение и нажимаю любую клавишу, я хочу захватить часть растрового изображения экрана.

Часть экрана, которая меня интересует, - это та, которую занимает мое окно, а именно содержимое верхнего прямоугольника в моем окне, которое содержит буквы. Захваченное растровое изображение экрана должно выглядеть следующим образом:

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

Проблема, с которой я сталкиваюсь, заключается в том, что код захвата экрана захватывает неправильную часть экрана.

Ниже приведен полный код (имейте в виду, что я старался держать вещи как можно меньше):

#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;
}


Как исправить расчетную "ошибку" (может быть, это не ошибка, может быть, я неправильно использую API?) с помощью MapWindowPoints?

ОБНОВЛЕНИЕ:

Я забыл упомянуть, что у меня есть 2 монитора. После тестирования приложения на втором мониторе все работало нормально.

Пройдя через настройки для первого монитора, я обнаружил, что он настроен на масштабирование текста, приложений и других элементов до 150%.

Возврат его к 100% заставил код работать, но теперь мне нужно найти решение для этого случая, так как я не могу заставить пользователей изменять свои настройки.

Любая помощь будет оценена по достоинству.

Что я уже пробовал:

Комментирование MapWindowPoints не помогло, ClientToScreen тоже не помог, и я не нашел альтернативного API...

Richard MacCutchan

Запустите код через отладчик, чтобы точно видеть, какие значения используются для координат.

MyOldAccount

Привет, Рич! Давно не виделись :)

Я прошел через отладчик, никаких ошибок, все "работает", просто координаты "неправильные".

Интересно, не злоупотребляю ли я API? Я действительно не знаю, в чем может быть проблема...

Richard MacCutchan

Ну, я понятия не имею, что вы подразумеваете под словом "неправильно".

Richard MacCutchan

Я только что запустил код для преобразования координат, и все ответы выглядят правильными.

Luc Pattyn

Я предлагаю вам запустить свой код на экране, который показывает большое (фоновое) изображение, чтобы вы могли видеть, какую (неправильную) часть экрана вы на самом деле захватываете; это значительно облегчит выяснение того, каким образом ваши координатные манипуляции терпят неудачу...

MyOldAccount

Спасибо за ваше предложение. Я думаю, что нашел проблему (см. обновление в нижней части поста), теперь мне нужно найти решение...

Richard MacCutchan

Я думаю, что вы, возможно, используете неправильные структуры RECT в своем обработчике WM_PAINT.

MyOldAccount

Я думаю, что нашел проблему (см. обновление в нижней части поста), теперь мне нужно найти решение...

3 Ответов

Рейтинг:
7

KarstenK

Вы должны уважать масштабирование, Как писал Ричард, а также иметь дело с системой мультимониторов. Несколько лет назад я написал статью: Переустановку Windows на системах с несколькими мониторами статья, посвященная этому вопросу.

Но тем не менее ваша функция foo выглядит правильной и нуждается только в усилении в этой манере.


Рейтинг:
0

Dave Kreskowiak

Он захватывает именно то, что ваш код говорит ему захватить. У вас нет другого выбора, кроме как запустить этот код под отладчиком, исследуя переменные, чтобы убедиться, что значения являются тем, что они должны быть.

Код не делает того, что вы думаете, что он делает. Отладчик предназначен для отладки вас и вашего понимания кода, чтобы вы могли изменить свое представление о том, как работает код, и улучшить его.


MyOldAccount

Спасибо за ваше предложение. Я думаю, что нашел проблему (см. обновление в нижней части поста), теперь мне нужно найти решение...