开启高DPI缩放时调用SetProcessDPIAware后Screen属性返回错误如何解决
问题原因
这不是Screen.PrimaryScreen.Bounds的Bug,原因是WinForms的Screen类会在首次访问时缓存当前进程的DPI感知上下文对应的屏幕数据,你在已经调用过Screen相关属性后才执行SetProcessDPIAware(),已经生成的缓存不会自动刷新,所以才会继续返回缩放后的错误值。
解决方法
1. 调整代码调用顺序,获取正确的Screen类数据
只需要把SetProcessDPIAware()的调用挪到所有引用System.Windows.Forms的代码之前,让Screen类初始化时就运行在DPI感知上下文下,就能拿到正确的所有显示器数据,调整后的PowerShell示例如下:
# 第一步先设置DPI感知,再加载任何Forms相关程序集 Add-Type -Name User32 -Namespace W32 ' [DllImport("user32.dll")] public static extern bool SetProcessDPIAware();' [W32.User32]::SetProcessDPIAware() # 之后再加载Forms程序集、调用Screen相关属性 Add-Type -AssemblyName System.Windows.Forms Clear-Host [Cursor]::Position | Write-Host [Screen]::PrimaryScreen.Bounds.Size | Write-Host [SystemInformation]::PrimaryMonitorSize | Write-Host # 遍历所有显示器也能拿到正确数据 foreach ($screen in [Screen]::AllScreens) { Write-Host "显示器 $($screen.DeviceName) 分辨率:$($screen.Bounds.Size)" }
C#代码同理,把SetProcessDPIAware()放到Main函数的第一行执行即可。
2. 为PowerShell/PowerShell ISE设置全局禁用高DPI缩放
不需要改代码也可以直接给程序设置默认DPI感知,操作步骤如下:
- 找到对应程序的可执行文件路径,默认路径为:
- PowerShell:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe - PowerShell ISE:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe
- PowerShell:
- 右键点击可执行文件,选择「属性」→「兼容性」→「更改高DPI设置」
- 勾选「替代高DPI缩放行为」,下拉框选择「应用程序」,保存设置即可。
也可以通过注册表批量设置,在路径HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers下新建字符串值,名称为对应程序的完整路径,数值数据填~ HIGHDPIAWARE即可。
3. 直接调用Win32 API获取多显示器数据
如果不想调整代码顺序或者修改程序兼容性设置,可以直接调用EnumDisplayDevices和EnumDisplaySettingsEx原生API获取所有显示器的物理分辨率,不受进程DPI感知状态的影响。
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

