Захват неправильной части экрана
Я создал небольшое демо-приложение, окно которого выглядит следующим образом:
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
Я думаю, что нашел проблему (см. обновление в нижней части поста), теперь мне нужно найти решение...