Winapi GetWindowRect返回一个大小,包括;无形的;边界

Winapi GetWindowRect返回一个大小,包括;无形的;边界,winapi,vb6,windows-10,dwm,Winapi,Vb6,Windows 10,Dwm,我正在开发一个应用程序,它以网格的方式将窗口定位在屏幕上。在Windows 10上运行此功能时,两个窗口之间存在巨大的差距。进一步的调查显示,GetWindowRect正在返回意外的值,包括一个不可见的边框,但我无法让它返回带有可见边框的真实值 1) 建议这是设计的,您可以通过链接winver=6“修复”它。我的环境不允许这样做,但我已尝试将PEMajorOperatingSystemVersion和MajorSubsystemVersion更改为6,但没有任何影响 2) 该线程还建议使用DWM

我正在开发一个应用程序,它以网格的方式将窗口定位在屏幕上。在Windows 10上运行此功能时,两个窗口之间存在巨大的差距。进一步的调查显示,
GetWindowRect
正在返回意外的值,包括一个不可见的边框,但我无法让它返回带有可见边框的真实值

1) 建议这是设计的,您可以通过链接winver=6“修复”它。我的环境不允许这样做,但我已尝试将PE
MajorOperatingSystemVersion
MajorSubsystemVersion
更改为6,但没有任何影响

2) 该线程还建议使用
DWMWA\u EXTENDED\u FRAME\u BOUNDS
DWMWA\u EXTENDED\u FRAME\u BOUNDS
从DWM中获取真实坐标,这很有效,但意味着在获取窗口坐标的任何地方都要进行更改。它也不允许设置值,这让我们可以反向设置窗口大小

3) 表明在这个过程中缺乏新闻部的意识。在清单中设置DPI感知标志或调用
setProcessDPIaware
都没有任何结果

4) 一时兴起,我还尝试添加WindowsVista、7、8、8.1和10兼容标志,以及Windows主题清单,但没有任何更改

这个窗口被移动到0x0,1280x1024,应该是为了填满整个屏幕,当我们返回查询坐标时,我们会得到相同的值。 不过,考虑到旧版本Windows的边框,该窗口实际上要窄14像素


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

Windows 10在左、右和底部都有薄薄的不可见边框,用于抓握鼠标调整大小。边框可能如下所示:
7,0,7,7
(左、上、右、下)

调用
SetWindowPos
将窗口置于此坐标时:
0,012801024

窗口将拾取这些精确的坐标,
GetWindowRect
将返回相同的坐标。但从视觉上看,窗口似乎在这里:
712731017

你可以愚弄窗户,让它到这里来:
-712871031

为此,我们得到了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`
除非有更简单的解决方案

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

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

如果您想让您的眼睛与Windows告诉您的内容相匹配,您可以尝试使用Aero-Lite主题暴露这些边框,使其再次可见:


您可以响应
WM\u NCCALCSIZE
消息,修改
WndProc
的默认行为以删除不可见的边框

如前所述,当
wParam
>0时,应请求
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
    }
(或在Windows 10及更高版本上)可能有用。这些函数将客户端矩形转换为窗口大小

我猜你不想重叠边界,所以这可能不是一个完整的解决方案——但它可能是解决方案的一部分,可能对遇到这个问题的其他人有用

下面是我的代码库中的一个快速片段,我成功地使用这些代码设置窗口大小以获得所需的客户端大小,请原谅错误处理宏:

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感知功能

对于最大化窗口或小窗口,您期望的坐标和得到的坐标是什么?@barmak我希望当我将其设置为0x0时,窗口位于左上角,而不是它实际显示的7x0。请参见屏幕截图。这是VB6还是VB.NET?@IInspectable我的测试代码是VB6,正如标记的那样,但问题也会影响Win32 API。再次说明,将子系统更改为6.0应该是可行的。明天我必须再次测试,因为我只更改PE标题。这只是变得更好。。。此方法仅在显示窗口后有效:(在此之前,DWM函数只返回与
GetWindowRect
相同的结果。哦,我没有想到。我只是想删除这个答案。我注意到它还有其他问题。请注意,
SetWindowPos
GetWindowRect
工作正常。您请求了边框,系统给出了您的边框。唯一的专业问题是在Windows 10中边框是不可见的,因此窗口似乎位于错误的位置。Visual Studio IDE有自己的工具窗口,当工具窗口停靠时,它不使用边框或自定义的
NC_PAINT
;当其工具窗口浮动时,它使用默认边框。我想您想要类似的东西吗?我希望Windows也这样做让我来处理窗口的真实可见矩形:)这个答案很有用,所以我不希望删除它,因为它是目前为止唯一的解决方案……请注意,
DwmGetWindowAttribute()
返回物理坐标,但
GetWindowRect()
返回逻辑坐标。因此,对于