GetWindowRect返回一个大小,包括" invisible"边界

时间:2015-12-07 17:20:49

标签: winapi vb6 windows-10 dwm

我正在开发一款以网格样式在屏幕上定位窗口的应用。在Windows 10上运行时,窗口之间存在巨大差距。进一步调查显示,GetWindowRect正在返回意外值,包括一个不可见的边框,但我无法通过可见边框返回实际值。

1)This thread表明这是设计的,你可以修复"通过链接winver = 6。我的环境不允许这样做但我已经尝试将PE MajorOperatingSystemVersionMajorSubsystemVersion更改为6而没有任何影响

2)同一个线程还建议使用DwmGetWindowAttributeDWMWA_EXTENDED_FRAME_BOUNDS来获取DWM的真实坐标,这可以正常工作,但意味着改变到达窗口坐标的所有位置。它也不允许设置值,让我们反转过程以便能够设置窗口大小。

3)This question表明在此过程中缺乏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让我使用真实的窗口坐标?

4 个答案:

答案 0 :(得分:19)

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`

除非有更简单的解决方案!

答案 1 :(得分:0)

  

如何说服Windows让我使用真实的窗口坐标?

您已经在使用真实坐标。 Windows10只是选择隐藏你眼睛的边框。但尽管如此,他们仍然存在。将鼠标悬停在窗口边缘后,光标将变为调整大小光标,这意味着它实际上仍在窗口上方。

如果您希望您的眼睛与Windows告诉您的相符,您可以尝试使用Aero Lite主题公开这些边框以便再次显示它们:

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

答案 2 :(得分:0)

AdjustWindowRectEx(或在Windows 10和更高版本的AdjustWindowRectExForDpi上)可能有用。这些功能会将客户端矩形转换为窗口大小。

我想您虽然不想重叠边界,所以这可能不是一个完整的解决方案-但这可能是解决方案的一部分,可能对遇到此问题的其他人有用。 / p>

这是我的代码库中的一个简短代码段,在该代码段中,我已成功地使用它们来设置窗口大小以获得所需的客户端大小,请原谅错误处理宏:

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的功能

答案 3 :(得分:-1)

您可以回复WM_NCCALCSIZE消息,修改WndProc的默认行为以删除不可见的边框。

this documentthis document解释时,wParam> 0,On request wParam.Rgrc[0]包含窗口的新坐标,当过程返回时,Response wParam.Rgrc[0]包含新客户矩形的坐标。

golang代码示例:

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
    }