Winapi GetWindowRect返回一个大小,包括;无形的;边界
我正在开发一个应用程序,它以网格的方式将窗口定位在屏幕上。在Windows 10上运行此功能时,两个窗口之间存在巨大的差距。进一步的调查显示,Winapi GetWindowRect返回一个大小,包括;无形的;边界,winapi,vb6,windows-10,dwm,Winapi,Vb6,Windows 10,Dwm,我正在开发一个应用程序,它以网格的方式将窗口定位在屏幕上。在Windows 10上运行此功能时,两个窗口之间存在巨大的差距。进一步的调查显示,GetWindowRect正在返回意外的值,包括一个不可见的边框,但我无法让它返回带有可见边框的真实值 1) 建议这是设计的,您可以通过链接winver=6“修复”它。我的环境不允许这样做,但我已尝试将PEMajorOperatingSystemVersion和MajorSubsystemVersion更改为6,但没有任何影响 2) 该线程还建议使用DWM
GetWindowRect
正在返回意外的值,包括一个不可见的边框,但我无法让它返回带有可见边框的真实值
1) 建议这是设计的,您可以通过链接winver=6“修复”它。我的环境不允许这样做,但我已尝试将PEMajorOperatingSystemVersion
和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]
包含窗口的新坐标,当过程返回时,ResponsewParam.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感知功能
GetWindowRect
相同的结果。哦,我没有想到。我只是想删除这个答案。我注意到它还有其他问题。请注意,SetWindowPos
和GetWindowRect
工作正常。您请求了边框,系统给出了您的边框。唯一的专业问题是在Windows 10中边框是不可见的,因此窗口似乎位于错误的位置。Visual Studio IDE有自己的工具窗口,当工具窗口停靠时,它不使用边框或自定义的NC_PAINT
;当其工具窗口浮动时,它使用默认边框。我想您想要类似的东西吗?我希望Windows也这样做让我来处理窗口的真实可见矩形:)这个答案很有用,所以我不希望删除它,因为它是目前为止唯一的解决方案……请注意,DwmGetWindowAttribute()
返回物理坐标,但GetWindowRect()
返回逻辑坐标。因此,对于