多系统通过切换器共享单显示器时PowerShell分辨率脚本失效求助
作为自动化工程师,我的自动化工具要求UI处于**Head状态(需UI可见)**且屏幕分辨率为指定值。我使用基于C# P/Invoke编写的PowerShell脚本设置分辨率,物理直连显示器时脚本正常运行,但多系统通过KVM切换器共享单显示器时,若未将切换器切换至目标系统,脚本无法生效;切换至目标系统则脚本正常运行。
原脚本如下:
Function Set-ScreenResolution { <# .Synopsis Sets the Screen Resolution of the primary monitor .Description Uses Pinvoke and ChangeDisplaySettings Win32API to make the change .Example Set-ScreenResolution -Width 1024 -Height 768 -Freq 60 #> param ( [Parameter(Mandatory=$true, Position = 0)] [int] $Width, [Parameter(Mandatory=$true, Position = 1)] [int] $Height, [Parameter(Mandatory=$true, Position = 2)] [int] $Freq ) $pinvokeCode = Add-Type $pinvokeCode -ErrorAction SilentlyContinue [Resolution.PrmaryScreenResolution]::ChangeResolution($width,$height,$freq) } Set-ScreenResolution -Width 1366 -Height 768 -Freq 60
Win32的ChangeDisplaySettings API仅对当前激活连接的显示器生效。当KVM切换器未切换到目标系统时,系统会检测到物理显示器断开连接,此时显示器处于非激活状态,API无法修改其分辨率。
方案1:安装虚拟显示器驱动
安装虚拟显示器驱动(如Microsoft Virtual Display Adapter),让系统始终认为有显示器处于激活状态。即使KVM未切换到目标系统,虚拟显示器也能保持UI可见状态,脚本可直接设置其分辨率。
修改后的脚本支持指定显示器设备名(需先通过EnumDisplayDevices获取虚拟显示器的设备名,通常为\\.\DISPLAY2或类似):
Function Set-ScreenResolution { <# .Synopsis Sets the Screen Resolution of the specified monitor .Description Uses Pinvoke and ChangeDisplaySettingsEx Win32API to make the change .Example Set-ScreenResolution -DeviceName "\\.\DISPLAY1" -Width 1024 -Height 768 -Freq 60 #> param ( [Parameter(Mandatory=$true)] [string] $DeviceName, [Parameter(Mandatory=$true, Position = 0)] [int] $Width, [Parameter(Mandatory=$true, Position = 1)] [int] $Height, [Parameter(Mandatory=$true, Position = 2)] [int] $Freq ) $pinvokeCode = @" using System; using System.Runtime.InteropServices; namespace Resolution { public class ScreenResolution { [DllImport("user32.dll")] public static extern int ChangeDisplaySettingsEx( string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, int dwFlags, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] public struct DEVMODE { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; public short dmSpecVersion; public short dmDriverVersion; public short dmSize; public short dmDriverExtra; public int dmFields; public int dmPositionX; public int dmPositionY; public int dmDisplayOrientation; public int dmDisplayFixedOutput; public short dmColor; public short dmDuplex; public short dmYResolution; public short dmTTOption; public short dmCollate; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmFormName; public short dmLogPixels; public int dmBitsPerPel; public int dmPelsWidth; public int dmPelsHeight; public int dmDisplayFlags; public int dmDisplayFrequency; public int dmICMMethod; public int dmICMIntent; public int dmMediaType; public int dmDitherType; public int dmReserved1; public int dmReserved2; public int dmPanningWidth; public int dmPanningHeight; } public static int ChangeResolution(string deviceName, int width, int height, int freq) { DEVMODE dm = new DEVMODE(); dm.dmSize = (short)Marshal.SizeOf(dm); dm.dmDeviceName = deviceName; dm.dmPelsWidth = width; dm.dmPelsHeight = height; dm.dmDisplayFrequency = freq; dm.dmFields = 0x00080000 | 0x00100000 | 0x00400000; // DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY return ChangeDisplaySettingsEx(deviceName, ref dm, IntPtr.Zero, 0, IntPtr.Zero); } } } "@ Add-Type $pinvokeCode -ErrorAction SilentlyContinue [Resolution.ScreenResolution]::ChangeResolution($DeviceName, $width, $height, $freq) } # 替换为你的虚拟显示器设备名 Set-ScreenResolution -DeviceName "\\.\DISPLAY2" -Width 1366 -Height 768 -Freq 60
方案2:开启KVM的EDID仿真功能
部分高端KVM切换器支持EDID仿真,该功能会向每个连接的系统发送虚拟的显示器EDID信息,让系统始终认为物理显示器处于连接状态。开启该功能后,无需切换KVM,脚本即可正常修改目标系统的分辨率。
方案3:使用DisplayConfig API配置(进阶)
使用Windows原生的DisplayConfig API(通过PowerShell调用),该API支持配置未激活的显示器。核心思路是通过DisplayConfigGetDeviceInfo获取显示器配置,修改分辨率后用DisplayConfigSetDeviceInfo应用设置,适合需要更精细控制的场景。
内容的提问来源于stack exchange,提问作者Deepak Kumar

