Получить точный размер области окна. Размер окна CreateWindow не соответствует размеру окна.

Я заметил что-то очень раздражающее при попытке создать окно на C++ и нарисовать прямоугольники, размер окна которых не соответствует размеру, который я установил.

Например, если я устанавливаю окно 480x240 и пытаюсь рисовать прямоугольники сверху вниз, слева направо, получая GetWindowRect(hwnd, &rect) и вычисляя ширину и высоту:

rectangle_width = (rect.right - rect.left) / amountRectangleX;
rectangle_height = (rect.bottom - rect.top) / amountRectangleY;

если amountRectangleX = 2 и Y = 2, он рисует 4 прямоугольника, но ширина и высота «выключены», поэтому он не заполняет весь экран или рендерится поверх него. Единственный способ, которым это может произойти (я сделал это на многих других языках, поэтому я знаю, что это работает), заключается в том, что если я установлю размер окна = 480x240, я хочу, чтобы это была область для «РИСЕНИЯ». Потому что, если границы включены в размер окна, это будет отличаться на другом компьютере с другим стилем окна и тому подобным. И я не могу просто «изменить» это вручную для своего компьютера.

Если я установлю размер окна = 480x240 и сделаю снимок экрана, я увижу, что пространство окна = 452x232, что сбивает с толку. Было бы нормально, если бы я установил размер окна = 480x240, но когда я GetWindowRect(), я получаю 452x232, а не 480x240, что тогда неверно, потому что у меня меньше места для рисования. Это объясняет, почему мои прямоугольники отображаются за пределами окна, а я этого НЕ хочу. Но я все еще хочу иметь возможность установить свой размер = 480x240 или что-то еще, но при этом иметь границы.

Почему это так работает и есть ли решение этой проблемы? Я не могу быть единственным, кто хочет иметь возможность устанавливать разрешение окна, и независимо от того, какой компьютер вы используете, установленный вами размер ЯВЛЯЕТСЯ ОБЛАСТЬЮ РИСОВАНИЯ, на которой вы можете рисовать.


person Deukalion    schedule 02.08.2012    source источник
comment
Не уверен, что вы хотите GetClientRect. Он получает часть, на которой вы можете рисовать, и исключает границу/заголовок/и т.д..   -  person chris    schedule 02.08.2012
comment
rect.right - rect.right? Уверены ли вы?   -  person Lol4t0    schedule 02.08.2012
comment
GetWindowRect() и GetClientRect() возвращают один и тот же результат.   -  person Deukalion    schedule 02.08.2012
comment
Я имел в виду rect.right - rect.left.   -  person Deukalion    schedule 02.08.2012
comment
GetClientRect() не должен возвращать тот же результат, что и GetWindowRect(). Я думаю, что вы упустили некоторые важные детали в своем вопросе. Вы рисуете границы окна на заказ? Вы используете аэро тему? Вы не забыли вызвать DefWindowProc в своей оконной процедуре?   -  person Peter Ruderman    schedule 02.08.2012


Ответы (3)


Я использую этот метод, и теперь он работает:

if ( IsWindow( hwnd ) )
{

DWORD dwStyle = GetWindowLongPtr( hwnd, GWL_STYLE ) ;
DWORD dwExStyle = GetWindowLongPtr( hwnd, GWL_EXSTYLE ) ;
HMENU menu = GetMenu( hwnd ) ;

RECT rc = { 0, 0, width, height } ;

AdjustWindowRectEx( &rc, dwStyle, menu ? TRUE : FALSE, dwExStyle );

SetWindowPos( hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOMOVE ) ;

}
person Deukalion    schedule 07.08.2012

Как и @Deukalion, я непреднамеренно вставил желаемый размер клиентской области в вызов ::CreateWindow(), и при рисовании сетки мне не хватило пикселей. Я применил тот же подход к исследованию размера снимка экрана моего приложения в пикселях. Я мог бы понять, что границы окна и строка заголовка отнимают некоторое пространство у клиентской области, но неприятно то, что в Windows 10 даже все окно приложения не имеет требуемого размера!

Я подозреваю, что ::CreateWindow() вычисляет меньшую клиентскую область, вычитая жестко заданное количество пикселей по ширине и высоте, а затем строит границу окна и строку заголовка вокруг этого. Это жестко запрограммированное число больше не соответствует ультратонкой теме окон Windows 10. Это смешно — на самом деле ничего на экране такого размера, который был запрошен в ::CreateWindow() !

Я последовал совету @PeterRuderman использовать вызов ::AdjustWindowRect():

RECT rect;
rect.left = rect.top = 0;
rect.right = clientWidth;
rect.bottom = clientHeight;
::AdjustWindowRect(&rect, wflags, false);
HWND hWindow = ::CreateWindow(wcName, title, wflags, 0, 0,
  rect.right - rect.left, rect.bottom - rect.top, 0, 0, hInstance, 0);
  • Как указано на https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897.aspx структура RECT рассматривает поля right и bottom как за пределами намеченного прямоугольника.
  • Было бы неплохо, если бы ::AdjustWindowRect() оставил заданные исходные координаты (0,0) нетронутыми, чтобы right и bottom могли быть непосредственно заполнены в вызове ::CreateWindow(). Но, к моему удивлению, поля left и bottom стали отрицательными.
person user1564286    schedule 24.12.2015

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

#include <Windows.h>

LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg ) {
    case WM_CLOSE:
        PostQuitMessage( 0 );
        break;

    case WM_PAINT:
        {
            RECT clientArea;
            GetClientRect( hwnd, &clientArea );

            PAINTSTRUCT ps;
            BeginPaint( hwnd, &ps );

            HBRUSH brush = (HBRUSH)GetStockObject( BLACK_BRUSH );

            RECT topLeft = clientArea;
            topLeft.right /= 2;
            topLeft.bottom /= 2;

            RECT bottomRight = clientArea;
            bottomRight.left = bottomRight.right / 2;
            bottomRight.top = bottomRight.bottom / 2;

            FillRect( ps.hdc, &topLeft, brush );
            FillRect( ps.hdc, &bottomRight, brush );

            EndPaint( hwnd, &ps );
        }

        return 0;
    }

    return DefWindowProc( hwnd, uMsg, wParam, lParam );
}

int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    // Error checking omitted for brevity.
    WNDCLASSEX wc = { 0 };

    wc.cbSize = sizeof( wc );
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = L"testclass";

    ATOM classAtom = RegisterClassEx( &wc );

    HWND window = CreateWindow( L"testclass", L"Test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 480, 240, NULL, NULL, hInstance, NULL );

    MSG msg;

    while( GetMessage( &msg, NULL, 0, 0 ) ) {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
}

Изменить

Перечитав ваш вопрос, я думаю, вы просто ищете API AdjustWindowRect. В этом случае ваш вопрос является дубликатом этого: WinAPI: создать окно с указанным размером клиентской области. Для дальнейшего использования «область, на которой вы можете рисовать», называется клиентской областью.

person Peter Ruderman    schedule 02.08.2012
comment
Мое окно, определенное как 480x240, во время выполнения: 452x232. Это клиентская область, так зачем определять окно, в котором вы должны рисовать, но вы не получаете точных значений, которые вы на самом деле установили? Этот язык просто раздражает. - person Deukalion; 06.08.2012
comment
Я понимаю, что поначалу это может сбивать с толку, но по мере знакомства с Win32 API все станет более понятным. Просто помните, что CreateWindow принимает размер окна, а не размер его клиентской области. - person Peter Ruderman; 06.08.2012
comment
Да, это сбивает с толку. Особенно, когда вы хотите нарисовать что-то вроде сетки на окне. - person Deukalion; 07.08.2012