GetWindowRect возвращает размер, включая "невидимые" границы

Я работаю над приложением, которое размещает окна на экране в стиле сетки. При запуске этого в Windows 10 между окнами существует огромный разрыв. Дальнейшие исследования показывают, что GetWindowRect возвращает неожиданные значения, в том числе невидимую границу, но я не могу заставить его возвращать реальные значения с видимой границей.

1) Эта ветка предполагает, что это сделано специально, и вы можете "исправить" это, связавшись с winver = 6. Моя среда не позволяет этого, но я попытался изменить PE MajorOperatingSystemVersion и MajorSubsystemVersion на 6 без каких-либо последствий

2) Тот же поток также предлагает использовать DwmGetWindowAttribute с DWMWA_EXTENDED_FRAME_BOUNDS для получения реальных координат из DWM, что работает, но означает изменение везде, где получаются координаты окна. Это также не позволяет устанавливать значение, что позволяет нам изменить процесс, чтобы иметь возможность устанавливать размер окна.

3) Этот вопрос предполагает отсутствие осведомленности о DPI в процессе. Ни установка флага осведомленности DPI в манифесте, ни вызов SetProcessDpiAwareness не дали никаких результатов.

4) Я также попытался добавить флаги совместимости Windows Vista, 7, 8, 8.1 и 10, и темы Windows проявляются без изменений.

Screenshot of a "fullscreen" window with gaps all round Это окно перемещается в 0x0, 1280x1024, предположительно, чтобы заполнить весь экран, и при запросе координат назад мы получаем те же значения. Однако на самом деле это окно на 14 пикселей уже, чтобы учесть границы в старых версиях Windows.

Как я могу убедить Windows разрешить мне работать с реальными координатами окна?

Ответ 1

Windows 10 имеет тонкие невидимые границы слева, справа и снизу, она используется для захвата мыши для изменения размера. Границы могут выглядеть так: 7,0,7,7 (левый, верхний, правый, нижний)

Когда вы вызываете SetWindowPos, чтобы поместить окно в эти координаты:
0, 0, 1280, 1024

Окно выберет те точные координаты, а GetWindowRect вернет те же координаты. Но визуально окно выглядит здесь:
7, 0, 1273, 1017

Вы можете обмануть окно и сказать ему вместо этого:
-7, 0, 1287, 1031

Для этого мы получаем толщину границы Windows 10:

RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));

//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`

RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;

//border should be `7, 0, 7, 7`

Затем смещаем прямоугольник следующим образом:

rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;

//new rect should be `-7, 0, 1287, 1031`

Если нет более простого решения!

Ответ 2

Как я могу убедить Windows позволить мне работать с реальными координатами окна?

Вы уже работаете с реальными координатами. Windows10 просто решил скрыть границы от ваших глаз. Но тем не менее они все еще там. Пройдя по краям окна, ваш курсор изменится на курсор изменения размера, что означает, что он все еще находится над окном.

Если вы хотите, чтобы ваши глаза соответствовали тому, что вам говорит Windows, вы можете попытаться разоблачить эти границы, чтобы они снова были видны, используя тему Aero Lite:

http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/

Ответ 3

AdjustWindowRectEx (или в Windows 10 и более поздних версиях AdjustWindowRectExForDpi) может быть полезным. Эти функции преобразуют прямоangularьник клиента в размер окна.

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

Вот быстрый фрагмент из моей кодовой базы, где я успешно использовал их, чтобы установить размер окна, чтобы получить желаемый размер клиента, извините макросы обработки ошибок:

DWORD window_style = (DWORD)GetWindowLong(global_context->window, GWL_STYLE);
CHECK_CODE(window_style);
CHECK(window_style != WS_OVERLAPPED); // Required by AdjustWindowRectEx

DWORD window_style_ex = (DWORD)GetWindowLong(global_context->window, GWL_EXSTYLE);
CHECK_CODE(window_style_ex);

// XXX: Use DPI aware version?
RECT requested_size = {};
requested_size.right = width;
requested_size.bottom = height;
AdjustWindowRectEx(
    &requested_size,
    window_style,
    false, // XXX: Why always false here?
    window_style_ex
);

UINT set_window_pos_flags = SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
CHECK_CODE(SetWindowPos(
    global_context->window,
    nullptr,
    0,
    0,
    requested_size.right - requested_size.left,
    requested_size.bottom - requested_size.top,
    set_window_pos_flags
));

В приведенном выше случае использования все еще есть две неясности:

  • В моем окне есть меню, но я должен передать false для параметра меню, иначе я получу неправильный размер. Я уточню этот ответ объяснением, если выясню, почему это так!
  • Я еще не читал о том, как Windows обрабатывает осведомленность о DPI, поэтому я не уверен, когда вы хотите использовать эту функцию по сравнению с функцией, не поддерживающей DPI

Ответ 4

Вы можете ответить на сообщение WM_NCCALCSIZE, изменить поведение WndProc по умолчанию, чтобы удалить невидимую границу.

Как этот документ и этот документ объясняет, когда wParam > 0, On request wParam.Rgrc[0] содержит новые координаты окна и когда процедура возвращается, Response wParam.Rgrc[0] содержит координаты нового клиентского прямоугольника.

Пример кода голанга:

case win.WM_NCCALCSIZE:
    log.Println("----------------- WM_NCCALCSIZE:", wParam, lParam)

    if wParam > 0 {
        params := (*win.NCCALCSIZE_PARAMS)(unsafe.Pointer(lParam))
        params.Rgrc[0].Top = params.Rgrc[2].Top
        params.Rgrc[0].Left = params.Rgrc[0].Left + 1
        params.Rgrc[0].Bottom = params.Rgrc[0].Bottom - 1
        params.Rgrc[0].Right = params.Rgrc[0].Right - 1
        return 0x0300
    }