请求修复PowerShell显示缩放配置脚本异常问题
问题分析与解决方案
你的问题源于对SPI_SETLOGICALDPIOVERRIDEAPI的参数理解错误,以及Windows 11对内置高DPI显示器的缩放限制:
- 参数误解:
SPI_SETLOGICALDPIOVERRIDE的uiParam参数需要传入DPI数值,而非自定义的缩放等级。缩放百分比与DPI的对应关系为:- 100% → 96
- 125% → 120
- 150% → 144
- 175% → 168
- 系统限制:Windows 11中,内置高DPI显示器通常带有系统默认的缩放锁定,
SPI_SETLOGICALDPIOVERRIDE无法直接覆盖该设置,而外接显示器无此限制,因此能正常生效。
方案一:修正API调用参数与刷新逻辑
使用正确的DPI值调用API,并添加系统刷新指令,确保设置生效:
function Configure-DisplayScaling { param ([int]$targetDpi) $sourceScale = @" using System; using System.Runtime.InteropServices; public class User32 { public const uint SPI_SETLOGICALDPIOVERRIDE = 0x009F; public const uint SPIF_UPDATEINIFILE = 0x01; public const uint SPIF_SENDCHANGE = 0x02; [DllImport("user32.dll", SetLastError = true)] public static extern bool SystemParametersInfo( uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni); } "@ Add-Type -TypeDefinition $sourceScale -Language CSharp # 发送设置更改并通知系统刷新 $success = [User32]::SystemParametersInfo( [User32]::SPI_SETLOGICALDPIOVERRIDE, [uint]$targetDpi, [IntPtr]::Zero, [User32]::SPIF_UPDATEINIFILE -bor [User32]::SPIF_SENDCHANGE ) if (-not $success) { Write-Error "设置失败,错误代码: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())" } } # 设置为125%缩放(对应120 DPI) Configure-DisplayScaling -targetDpi 120
方案二:使用DisplayConfig API针对单显示器设置
如果方案一无法修改内置显示器缩放,可使用更底层的DisplayConfig API直接操作每个显示器的缩放设置,绕过系统锁定:
function Set-DisplayScaling { param( [int]$ScalingPercent # 传入100, 125, 150等百分比值 ) $source = @" using System; using System.Runtime.InteropServices; public enum DISPLAYCONFIG_SCALING : uint { DISPLAYCONFIG_SCALING_IDENTITY = 1, DISPLAYCONFIG_SCALING_CENTERED = 2, DISPLAYCONFIG_SCALING_STRETCHED = 3, DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX = 4, DISPLAYCONFIG_SCALING_CUSTOM = 5, DISPLAYCONFIG_SCALING_PREFERRED = 128 } public enum DISPLAYCONFIG_DEVICE_INFO_TYPE : uint { DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_SCALING = 10 } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_TARGET_INFO { public LUID adapterId; public uint id; public uint modeInfoIdx; public DISPLAYCONFIG_SCALING scaling; public uint rotation; public uint desktopOrientations; public uint targetAvailable; public uint statusFlags; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_SET_TARGET_SCALING { public DISPLAYCONFIG_DEVICE_INFO_HEADER header; public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; public uint scaling; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_DEVICE_INFO_HEADER { public DISPLAYCONFIG_DEVICE_INFO_TYPE type; public uint size; public LUID adapterId; public uint id; } [StructLayout(LayoutKind.Sequential)] public struct LUID { public uint LowPart; public int HighPart; } public class DisplayConfigApi { [DllImport("user32.dll", SetLastError = true)] public static extern int GetDisplayConfigBufferSizes( uint flags, out uint pathCount, out uint modeInfoCount); [DllImport("user32.dll", SetLastError = true)] public static extern int QueryDisplayConfig( uint flags, ref uint pathCount, IntPtr pathArray, ref uint modeInfoCount, IntPtr modeInfoArray, IntPtr currentTopologyId); [DllImport("user32.dll", SetLastError = true)] public static extern int DisplayConfigSetDeviceInfo(ref DISPLAYCONFIG_SET_TARGET_SCALING deviceInfo); } "@ Add-Type -TypeDefinition $source -Language CSharp # 获取显示配置缓冲区大小 $pathCount = 0 $modeInfoCount = 0 $result = [DisplayConfigApi]::GetDisplayConfigBufferSizes(0x00000002, [ref]$pathCount, [ref]$modeInfoCount) if ($result -ne 0) { Write-Error "获取显示配置缓冲区失败,错误代码: $result" return } # 分配内存存储路径信息 $pathSize = [System.Runtime.InteropServices.Marshal]::SizeOf([Type][DisplayConfigApi+DISPLAYCONFIG_PATH_TARGET_INFO]) * $pathCount $pathArray = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($pathSize) try { # 查询当前显示路径配置 $result = [DisplayConfigApi]::QueryDisplayConfig(0x00000002, [ref]$pathCount, $pathArray, [ref]$modeInfoCount, [IntPtr]::Zero, [IntPtr]::Zero) if ($result -ne 0) { Write-Error "查询显示配置失败,错误代码: $result" return } # 遍历每个显示器设置缩放 for ($i = 0; $i -lt $pathCount; $i++) { $targetInfoPtr = [IntPtr]::Add($pathArray, $i * [System.Runtime.InteropServices.Marshal]::SizeOf([Type][DisplayConfigApi+DISPLAYCONFIG_PATH_TARGET_INFO])) $targetInfo = [DisplayConfigApi+DISPLAYCONFIG_PATH_TARGET_INFO][System.Runtime.InteropServices.Marshal]::PtrToStructure($targetInfoPtr, [Type][DisplayConfigApi+DISPLAYCONFIG_PATH_TARGET_INFO]) # 根据百分比匹配缩放枚举值 $scalingEnum = switch ($ScalingPercent) { 100 { [DisplayConfigApi+DISPLAYCONFIG_SCALING]::DISPLAYCONFIG_SCALING_IDENTITY } 125,150,175 { [DisplayConfigApi+DISPLAYCONFIG_SCALING]::DISPLAYCONFIG_SCALING_CUSTOM } default { [DisplayConfigApi+DISPLAYCONFIG_SCALING]::DISPLAYCONFIG_SCALING_PREFERRED } } # 构造缩放设置结构体 $deviceInfo = New-Object DisplayConfigApi+DISPLAYCONFIG_SET_TARGET_SCALING $deviceInfo.header.type = [DisplayConfigApi+DISPLAYCONFIG_DEVICE_INFO_TYPE]::DISPLAYCONFIG_DEVICE_INFO_SET_TARGET_SCALING $deviceInfo.header.size = [System.Runtime.InteropServices.Marshal]::SizeOf($deviceInfo) $deviceInfo.header.adapterId = $targetInfo.adapterId $deviceInfo.header.id = $targetInfo.id $deviceInfo.targetInfo = $targetInfo $deviceInfo.scaling = [uint]$scalingEnum # 应用缩放设置 $result = [DisplayConfigApi]::DisplayConfigSetDeviceInfo([ref]$deviceInfo) if ($result -eq 0) { Write-Host "显示器 $($targetInfo.id) 缩放已设置为 $ScalingPercent%" } else { Write-Warning "显示器 $($targetInfo.id) 缩放设置失败,错误代码: $result" } } } finally { # 释放内存 [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pathArray) } } # 设置所有显示器为125%缩放 Set-DisplayScaling -ScalingPercent 125
内容的提问来源于stack exchange,提问作者LysTheIT
相关产品推荐
相关产品推荐

