Screen.PrimaryScreen.Bounds.Width 和 Height 是 WinForms 下获取主显示器原始分辨率最直接的方式,返回设备像素值,但需注意 DPI 意识、GUI 上下文及多屏适配场景。

用 Screen.PrimaryScreen.Bounds 读主显示器尺寸最直接
主显示器的宽高信息就藏在 Screen.PrimaryScreen.Bounds 的 Width 和 Height 属性里,这是 WinForms 环境下最轻量、最可靠的获取方式。
常见错误是误用 Screen.AllScreens 去遍历再判断主屏——没必要,PrimaryScreen 已经封装好逻辑;另一个坑是取了 WorkingArea(扣掉任务栏),但用户要的是“分辨率”,不是“可用工作区”。
-
Screen.PrimaryScreen.Bounds.Width和.Height返回的是像素值,单位是 device pixel,和 DPI 缩放无关 - 如果应用启用了 Per-Monitor DPI Awareness,
Bounds仍返回逻辑像素(即系统缩放后看到的尺寸),不是物理像素——这点常被忽略 - 该属性在无 GUI 的上下文(如 Windows Service 或 .NET Core 控制台未引用 WinForms)中会抛
NullReferenceException,因为PrimaryScreen为 null
WPF 里别用 SystemParameters 查分辨率
SystemParameters.PrimaryScreenWidth 和 PrimaryScreenHeight 看起来方便,但它们返回的是缩放前的逻辑像素(DPI=96 下的值),在 125% / 150% 缩放时严重失真,实际值比屏幕显示的小。
真正要用 WPF 获取真实像素尺寸,得靠 VisualTreeHelper.GetDpi() 配合 SystemParameters 反推,或者更稳妥地:跨到 Win32 API 调用 GetMonitorInfo ——但多数场景没必要这么重。
- 如果只是做界面适配,优先用
RenderSize或ActualWidth/ActualHeight拿当前窗口尺寸 -
SystemParameters.VirtualScreenWidth是多屏拼接总宽,不是主屏宽度,别混淆 - WPF 应用若未声明 DPI 意识(
dpiAwareness="PerMonitorV2"),SystemParameters的值可能被系统强制缩放,结果不可信
多显示器环境下,Screen.FromControl 比 PrimaryScreen 更实用
很多程序不是只跑在主屏上,比如拖动窗口到副屏后还要动态适配尺寸。这时候硬写死 PrimaryScreen 就会出错。
Screen.FromControl(this)(WinForms)或 Screen.FromHandle(hWnd)(通用)能精准定位当前窗体所在的物理屏幕,再取 .Bounds 才是真实所见。
- WinForms 中,
FromControl(this)在窗体Load事件里调用是安全的;但在构造函数里调用可能因句柄未创建而返回 null - WPF 没有直接等价 API,需用
WindowInteropHelper先拿到Hwnd,再调Screen.FromHandle - 注意:副屏可能旋转(90°/270°),
Bounds的Width/Height依然按“自然方向”返回(即旋转后逻辑宽高已交换),不体现旋转状态
控制台程序或无窗场景下,只能走 Win32 API
没有 System.Windows.Forms 引用,或运行在 headless 环境(如 CI 服务器),Screen 类完全不可用。此时必须 P/Invoke GetDC + GetDeviceCaps,或更推荐的 EnumDisplayMonitors + GetMonitorInfo。
关键点在于:不能只查第一个显示器,得遍历并识别 MONITORINFO.dwFlags & MONITORINFOF_PRIMARY 才能确认主屏。
-
GetSystemMetrics(SM_CXSCREEN)只返回主屏宽度,但它是过时 API,在多 DPI 场景下返回值不准确 - 调用
GetMonitorInfo前必须确保cbSize字段设为Marshal.SizeOf<MONITORINFO>(),否则结构体读取越界,容易崩溃 - .NET 6+ 可用
Microsoft.Win32.SystemEvents监听DisplaySettingsChanged,但仅限有桌面会话的进程,服务账户下无效










