GetWindowRect returns 包含 "invisible" 边框的尺寸
GetWindowRect returns a size including "invisible" borders
我正在开发一款以网格样式在屏幕上定位 windows 的应用程序。当运行这对Windows10时,windows之间存在着巨大的差距。进一步的调查显示 GetWindowRect
是 return 意想不到的值,包括不可见的边框,但我无法将其变为 return 具有可见边框的实际值。
1) This thread 表明这是设计使然,您可以通过与 winver=6 链接来 "fix" 它。我的环境不允许这样做,但我已经尝试将 PE MajorOperatingSystemVersion
和 MajorSubsystemVersion
更改为 6 而不影响
2) 同一线程还建议使用 DwmGetWindowAttribute
和 DWMWA_EXTENDED_FRAME_BOUNDS
从 DWM 获取真实坐标,这有效,但意味着在获取 window 坐标的任何地方都需要更改。它还不允许设置值,让我们逆向过程才能设置 window 大小。
3) 表明它在这个过程中缺乏 DPI 意识。在清单中设置 DPI 感知标志或调用 SetProcessDpiAwareness
都没有任何结果。
4) 一时兴起,我还尝试添加 Windows Vista、7、8、8.1 和 10 兼容性标志,Windows 主题清单没有任何变化。
这个 window 被移动到 0x0, 1280x1024,据说会填满整个屏幕,当查询坐标回来时,我们得到相同的值。
然而 window 实际上窄了 14 个像素,以考虑旧版本 Windows 的边框。
如何说服 Windows 让我使用真实的 window 坐标?
Windows 10 左、右、下都有细细的隐形边框,用来夹住鼠标调整大小。边框可能如下所示:7,0,7,7
(左、上、右、下)
当您调用 SetWindowPos
将 window 置于此坐标时:
0, 0, 1280, 1024
window 将选择那些精确的坐标,而 GetWindowRect
将 return 选择相同的坐标。但在视觉上,window 似乎在这里:
7, 0, 1273, 1017
你可以欺骗 window 并告诉它去这里:
-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`
除非有更简单的解决方案!
How can I convince Windows to let me work with the real window coordinates?
您已经在使用真实坐标。 Windows10 只是选择隐藏边框不让您看到。但尽管如此,它们仍然存在。将鼠标悬停在 window 的边缘,您的光标将变为调整大小的光标,这意味着它实际上仍在 window.
上方
如果您希望您的眼睛与 Windows 告诉您的内容相符,您可以尝试使用 Aero Lite 主题暴露这些边框,以便它们再次可见:
http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/
您可以回复WM_NCCALCSIZE
消息,修改WndProc
的默认行为以移除不可见边框。
正如this document and this document所解释的那样,当wParam
> 0时,根据请求wParam.Rgrc[0]
包含window的新坐标并且当程序returns时,响应 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
}
AdjustWindowRectEx
(or on Windows 10 and later AdjustWindowRectExForDpi
) 可能有用。这些函数会将客户端矩形转换为 window 大小。
我猜你不想重叠边界,所以这可能不是一个完整的解决方案——但它可能是解决方案的一部分,并且可能对遇到这个问题的其他人有用.
这是我的代码库中的一个快速片段,我已经成功地使用它们来设置 window 大小以获得所需的客户端大小,请原谅错误处理宏:
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
));
上面的用例还有两点不明确:
- 我的 window 有一个菜单,但我必须为菜单参数传递 false,否则我会得到错误的尺寸。如果我弄清楚这是为什么,我会用解释更新这个答案!
- 我还没有读到 Windows 如何处理 DPI 感知,所以我不确定你什么时候想使用该功能与非 DPI 感知功能
我正在开发一款以网格样式在屏幕上定位 windows 的应用程序。当运行这对Windows10时,windows之间存在着巨大的差距。进一步的调查显示 GetWindowRect
是 return 意想不到的值,包括不可见的边框,但我无法将其变为 return 具有可见边框的实际值。
1) This thread 表明这是设计使然,您可以通过与 winver=6 链接来 "fix" 它。我的环境不允许这样做,但我已经尝试将 PE MajorOperatingSystemVersion
和 MajorSubsystemVersion
更改为 6 而不影响
2) 同一线程还建议使用 DwmGetWindowAttribute
和 DWMWA_EXTENDED_FRAME_BOUNDS
从 DWM 获取真实坐标,这有效,但意味着在获取 window 坐标的任何地方都需要更改。它还不允许设置值,让我们逆向过程才能设置 window 大小。
3) SetProcessDpiAwareness
都没有任何结果。
4) 一时兴起,我还尝试添加 Windows Vista、7、8、8.1 和 10 兼容性标志,Windows 主题清单没有任何变化。
如何说服 Windows 让我使用真实的 window 坐标?
Windows 10 左、右、下都有细细的隐形边框,用来夹住鼠标调整大小。边框可能如下所示:7,0,7,7
(左、上、右、下)
当您调用 SetWindowPos
将 window 置于此坐标时:
0, 0, 1280, 1024
window 将选择那些精确的坐标,而 GetWindowRect
将 return 选择相同的坐标。但在视觉上,window 似乎在这里:
7, 0, 1273, 1017
你可以欺骗 window 并告诉它去这里:
-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`
除非有更简单的解决方案!
How can I convince Windows to let me work with the real window coordinates?
您已经在使用真实坐标。 Windows10 只是选择隐藏边框不让您看到。但尽管如此,它们仍然存在。将鼠标悬停在 window 的边缘,您的光标将变为调整大小的光标,这意味着它实际上仍在 window.
上方如果您希望您的眼睛与 Windows 告诉您的内容相符,您可以尝试使用 Aero Lite 主题暴露这些边框,以便它们再次可见:
http://winaero.com/blog/enable-the-hidden-aero-lite-theme-in-windows-10/
您可以回复WM_NCCALCSIZE
消息,修改WndProc
的默认行为以移除不可见边框。
正如this document and this document所解释的那样,当wParam
> 0时,根据请求wParam.Rgrc[0]
包含window的新坐标并且当程序returns时,响应 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
}
AdjustWindowRectEx
(or on Windows 10 and later AdjustWindowRectExForDpi
) 可能有用。这些函数会将客户端矩形转换为 window 大小。
我猜你不想重叠边界,所以这可能不是一个完整的解决方案——但它可能是解决方案的一部分,并且可能对遇到这个问题的其他人有用.
这是我的代码库中的一个快速片段,我已经成功地使用它们来设置 window 大小以获得所需的客户端大小,请原谅错误处理宏:
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
));
上面的用例还有两点不明确:
- 我的 window 有一个菜单,但我必须为菜单参数传递 false,否则我会得到错误的尺寸。如果我弄清楚这是为什么,我会用解释更新这个答案!
- 我还没有读到 Windows 如何处理 DPI 感知,所以我不确定你什么时候想使用该功能与非 DPI 感知功能