Per-Monitor DPI Awareness V2 非96DPI下按客户区尺寸计算窗口尺寸问题
Per-Monitor DPI Awareness V2 环境下窗口尺寸计算正确方案
问题根因
你遇到的偏差本质是AdjustWindowRectExForDpi返回的窗口尺寸默认包含了窗口外围不可见的阴影扩展边框区域,这部分区域不会被算到实际显示的窗口大小里,也不会占用客户区空间,直接用该返回值设置窗口尺寸就会导致客户区比预期小,需要额外扣除这部分不可见边框的偏移量,不需要硬编码修正值。
正确实现步骤
- 第一步:先将目标客户区尺寸写入RECT结构体,设置
Left = 0、Top = 0,Right = 目标客户区宽度、Bottom = 目标客户区高度 - 第二步:调用
AdjustWindowRectExForDpi获取包含扩展边框的窗口总尺寸 - 第三步:调用
DwmGetWindowAttribute传入DWMWA_EXTENDED_FRAME_BOUNDS参数,获取当前窗口在对应DPI下的实际可见边框边界,计算不可见区域的偏移值 - 第四步:用偏移值修正
AdjustWindowRectExForDpi返回的RECT,得到最终需要的窗口尺寸
完整VB实现代码
API声明
Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Private Const GWL_STYLE As Long = -16 Private Const GWL_EXSTYLE As Long = -20 Private Const DWMWA_EXTENDED_FRAME_BOUNDS As Long = 9 Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" (ByVal hwnd As Long, ByVal dwAttribute As Long, ByRef pvAttribute As Any, ByVal cbAttribute As Long) As Long Private Declare Function GetWindowLong Lib "user32.dll" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long Private Declare Function AdjustWindowRectExForDpi Lib "user32.dll" (ByRef lpRect As RECT, ByVal dwStyle As Long, ByVal bMenu As Long, ByVal dwExStyle As Long, ByVal dpi As Long) As Long Private Declare Function GetDpiForWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
尺寸计算逻辑
Function CalcWindowSizeByClientSize(ByVal mHwnd As Long, ByVal targetClientWidth As Long, ByVal targetClientHeight As Long) As RECT Dim rClient As RECT, rAdjusted As RECT, rFrameBounds As RECT Dim dpi As Long, style As Long, exStyle As Long Dim borderOffsetX As Long, borderOffsetY As Long ' 初始化目标客户区尺寸 rClient.Left = 0 rClient.Top = 0 rClient.Right = targetClientWidth rClient.Bottom = targetClientHeight ' 获取窗口当前DPI、样式 dpi = GetDpiForWindow(mHwnd) style = GetWindowLong(mHwnd, GWL_STYLE) exStyle = GetWindowLong(mHwnd, GWL_EXSTYLE) ' 调用AdjustWindowRectExForDpi计算包含扩展边框的窗口尺寸 rAdjusted = rClient AdjustWindowRectExForDpi rAdjusted, style, 0, exStyle, dpi ' 获取实际可见的边框边界,计算不可见阴影的偏移量 DwmGetWindowAttribute mHwnd, DWMWA_EXTENDED_FRAME_BOUNDS, rFrameBounds, Len(rFrameBounds) borderOffsetX = (rAdjusted.Right - rAdjusted.Left) - (rFrameBounds.Right - rFrameBounds.Left) borderOffsetY = (rAdjusted.Bottom - rAdjusted.Top) - (rFrameBounds.Bottom - rFrameBounds.Top) ' 修正得到最终窗口尺寸 rAdjusted.Right = rAdjusted.Right - borderOffsetX rAdjusted.Bottom = rAdjusted.Bottom - borderOffsetY CalcWindowSizeByClientSize = rAdjusted End Function
注意事项
该逻辑必须在窗口已经完成初始化、并且附着到对应DPI的显示器之后调用,不要在Form_Load事件刚触发时执行,此时窗口DPI还未完全确定,拿到的偏移值会不准,建议在WM_DPICHANGED或者WM_ACTIVATE消息回调中执行计算。
内容的提问来源于stack exchange,提问作者Symundo
相关产品推荐
相关产品推荐

