能直接用,但需注意其返回主显示器物理像素而非逻辑像素,未设dpi感知时易与getclientrect等api单位不匹配,导致全屏黑边或ui模糊;多屏应改用enumdisplaymonitors+getmonitorinfo。

GetSystemMetrics(SM_CXSCREEN) 和 SM_CYSCREEN 能否直接用?
能,但要注意这是获取主显示器的原始分辨率(不含任务栏),不是当前 DPI 缩放后的逻辑像素。如果你在高 DPI 显示器上运行程序且未声明 DPI 感知,GetSystemMetrics 返回的值可能被系统缩放“欺骗”,比如物理 3840×2160 屏幕启用了 200% 缩放时,它仍返回 3840 和 2160 —— 这是正确的物理像素,但和你用 GetClientRect 看到的窗口尺寸单位不一致。
常见错误现象:SetWindowPos 设置窗口为全屏时留黑边,或窗口拉伸后 UI 元素模糊,往往就是混用了物理像素和 DPI 缩放后的逻辑像素。
- 若只需主屏宽高(如初始化全屏渲染区),
GetSystemMetrics(SM_CXSCREEN)和SM_CYSCREEN最快、最轻量 - 若需考虑多显示器,必须用
EnumDisplayMonitors+GetMonitorInfo - 若程序未设置 DPI 感知(manifest 缺失或没调
SetProcessDpiAwareness),GetSystemMetrics值虽物理正确,但和其他 API(如GetClientRect)返回的客户区尺寸单位不匹配
如何用 GetMonitorInfo 获取指定显示器的真实工作区域?
GetMonitorInfo 返回的是 MONITORINFO 结构体,其中 rcWork 字段包含排除任务栏后的可用区域(例如任务栏在底部时,rcWork.bottom 小于 rcMonitor.bottom),比单纯用 rcMonitor 更贴近实际可绘制空间。
使用场景:做无边框窗口居中、最大化到工作区、或适配不同任务栏位置(左/右/顶部/自动隐藏)。
立即学习“C++免费学习笔记(深入)”;
MONITORINFO mi{ sizeof(mi) };
if (GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST), &mi)) {
int work_width = mi.rcWork.right - mi.rcWork.left;
int work_height = mi.rcWork.bottom - mi.rcWork.top;
}- 务必初始化
mi.cbSize = sizeof(mi),否则GetMonitorInfo失败返回 false -
MonitorFromWindow的第二个参数建议用MONITOR_DEFAULTTONEAREST,避免 hwnd 为 NULL 或无效句柄时出错 -
rcWork是屏幕坐标系下的矩形,单位是像素(物理像素),与当前 DPI 设置无关
SetProcessDpiAwareness 为什么必须在 GetSystemMetrics 前调用?
因为 GetSystemMetrics 的行为受进程 DPI 感知级别影响:未感知时,Windows 会将返回值除以缩放比例(如 200% 时除以 2),使数值看起来“符合逻辑像素”;而设置了 PROCESS_PER_MONITOR_DPI_AWARE 后,它才稳定返回物理像素。
容易踩的坑:在 WinMain 开头没调用 DPI 设置,却在窗口创建后读取 SM_CXSCREEN,结果发现数值忽大忽小,尤其在多显示器不同缩放率环境下。
- 推荐在
WinMain第一行就调用:SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) - 如果项目必须兼容 Win7,改用
SetProcessDPIAware(),但它只支持全局 DPI 感知,无法处理混合缩放 - manifest 文件里也要同步声明
dpiAware=true/pm,否则部分 Windows 版本忽略 API 调用
GetClientRect 和 GetWindowRect 返回值单位是否一致?
一致,都是像素,但坐标系不同:GetClientRect 返回客户区大小(不含边框、标题栏),原点是窗口左上角;GetWindowRect 返回整个窗口外边界,原点是屏幕左上角。两者都不受 DPI 感知设置影响 —— 它们始终返回当前缩放下的逻辑像素(即你看到的像素)。
所以,当你想让一个子窗口填满父窗口客户区,应该用 GetClientRect;想把窗口移到屏幕某位置,该用 GetWindowRect 配合 SetWindowPos。
- 不要拿
GetClientRect的宽度去和SM_CXSCREEN直接比较,除非已确保 DPI 感知一致 - 拖动窗口时实时获取尺寸,优先用
WM_SIZE消息里的lParam(LOWORD/HIWORD),比反复调用 API 更高效 - 如果窗口有 WS_THICKFRAME 样式,
GetClientRect的尺寸会随用户拖拽实时变化,无需额外刷新
实际写的时候最容易忽略的是 DPI 感知和 monitor info 初始化顺序 —— 很多人先读了分辨率再设 DPI 感知,结果白设了。











