Windows下能否为双显示器布局切换设置快捷键?
双屏布局快速切换的PowerShell实现
核心思路
借助Windows系统user32.dll提供的显示配置API,通过PowerShell脚本修改屏幕位置参数,再将脚本绑定到快捷键,实现一键切换两种布局。
步骤1:获取显示器设备信息
先运行以下命令,获取你的飞利浦显示器和Huion Kamvas的设备ID(后续脚本需要用到):
Get-CimInstance Win32_DesktopMonitor | Select-Object DeviceID, ScreenWidth, ScreenHeight
记录下格式类似DISPLAY\PHL****\****的设备ID。
步骤2:编写布局切换脚本
脚本1:垂直堆叠布局(Kamvas在主显示器下方)
新建文件VerticalStack.ps1,填入以下内容(替换<主显示器DeviceID>和<Kamvas DeviceID>为你获取的实际值):
Add-Type @" using System; using System.Runtime.InteropServices; public class DisplayConfig { [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_INFO { public DISPLAYCONFIG_PATH_SOURCE_INFO sourceInfo; public DISPLAYCONFIG_PATH_TARGET_INFO targetInfo; public uint flags; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_SOURCE_INFO { public uint adapterIdLow; public uint adapterIdHigh; public uint id; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_PATH_TARGET_INFO { public uint adapterIdLow; public uint adapterIdHigh; public uint id; public uint modeInfoIdx; public DISPLAYCONFIG_VIDEO_OUTPUT_TYPE outputType; public DISPLAYCONFIG_ROTATION rotation; public DISPLAYCONFIG_SCALING scaling; public DISPLAYCONFIG_RATIONAL refreshRate; public DISPLAYCONFIG_SCANLINE_ORDERING scanlineOrdering; public bool targetAvailable; public uint statusFlags; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_MODE_INFO { public DISPLAYCONFIG_MODE_INFO_TYPE infoType; public uint id; public uint adapterIdLow; public uint adapterIdHigh; public DISPLAYCONFIG_TARGET_MODE targetMode; public DISPLAYCONFIG_SOURCE_MODE sourceMode; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_TARGET_MODE { public DISPLAYCONFIG_VIDEO_SIGNAL_INFO targetVideoSignalInfo; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_SOURCE_MODE { public uint width; public uint height; public DISPLAYCONFIG_PIXELFORMAT pixelFormat; public POINTL position; } [StructLayout(LayoutKind.Sequential)] public struct POINTL { public int x; public int y; } public enum DISPLAYCONFIG_MODE_INFO_TYPE { DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE = 1, DISPLAYCONFIG_MODE_INFO_TYPE_TARGET = 2 } public enum DISPLAYCONFIG_ROTATION { DISPLAYCONFIG_ROTATION_IDENTITY = 1, DISPLAYCONFIG_ROTATION_ROTATE90 = 2, DISPLAYCONFIG_ROTATION_ROTATE180 = 3, DISPLAYCONFIG_ROTATION_ROTATE270 = 4 } public enum DISPLAYCONFIG_PIXELFORMAT { DISPLAYCONFIG_PIXELFORMAT_8BPP = 1, DISPLAYCONFIG_PIXELFORMAT_16BPP = 2, DISPLAYCONFIG_PIXELFORMAT_24BPP = 3, DISPLAYCONFIG_PIXELFORMAT_32BPP = 4, DISPLAYCONFIG_PIXELFORMAT_NONGDI = 5 } public enum DISPLAYCONFIG_VIDEO_OUTPUT_TYPE { DISPLAYCONFIG_OUTPUT_TYPE_OTHER = 0, DISPLAYCONFIG_OUTPUT_TYPE_HD15 = 1, DISPLAYCONFIG_OUTPUT_TYPE_SVIDEO = 2, DISPLAYCONFIG_OUTPUT_TYPE_COMPOSITE_VIDEO = 3, DISPLAYCONFIG_OUTPUT_TYPE_COMPONENT_VIDEO = 4, DISPLAYCONFIG_OUTPUT_TYPE_DVI = 5, DISPLAYCONFIG_OUTPUT_TYPE_HDMI = 6, DISPLAYCONFIG_OUTPUT_TYPE_LVDS = 7, DISPLAYCONFIG_OUTPUT_TYPE_D_JPN = 8, DISPLAYCONFIG_OUTPUT_TYPE_SDI = 9, DISPLAYCONFIG_OUTPUT_TYPE_DISPLAYPORT_EXTERNAL = 10, DISPLAYCONFIG_OUTPUT_TYPE_DISPLAYPORT_EMBEDDED = 11, DISPLAYCONFIG_OUTPUT_TYPE_UDI_EXTERNAL = 12, DISPLAYCONFIG_OUTPUT_TYPE_UDI_EMBEDDED = 13, DISPLAYCONFIG_OUTPUT_TYPE_SDTVDONGLE = 14, DISPLAYCONFIG_OUTPUT_TYPE_MIRACAST = 15 } public enum DISPLAYCONFIG_SCALING { DISPLAYCONFIG_SCALING_IDENTITY = 1, DISPLAYCONFIG_SCALING_CENTERED = 2, DISPLAYCONFIG_SCALING_STRETCHED = 3, DISPLAYCONFIG_SCALING_ASPECTRATIOCENTEREDMAX = 4, DISPLAYCONFIG_SCALING_CUSTOM = 5, DISPLAYCONFIG_SCALING_PREFERRED = 128 } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_RATIONAL { public uint Numerator; public uint Denominator; } public enum DISPLAYCONFIG_SCANLINE_ORDERING { DISPLAYCONFIG_SCANLINE_ORDERING_UNSPECIFIED = 0, DISPLAYCONFIG_SCANLINE_ORDERING_PROGRESSIVE = 1, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED = 2, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_UPPERFIELDFIRST = 2, DISPLAYCONFIG_SCANLINE_ORDERING_INTERLACED_LOWERFIELDFIRST = 3 } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_VIDEO_SIGNAL_INFO { public ulong pixelRate; public DISPLAYCONFIG_RATIONAL hSyncFreq; public DISPLAYCONFIG_RATIONAL vSyncFreq; public DISPLAYCONFIG_2DREGION activeSize; public DISPLAYCONFIG_2DREGION totalSize; public uint videoStandard; public DISPLAYCONFIG_SCANLINE_ORDERING scanLineOrdering; } [StructLayout(LayoutKind.Sequential)] public struct DISPLAYCONFIG_2DREGION { public uint cx; public uint cy; } [DllImport("user32.dll")] public static extern long GetDisplayConfigBufferSizes(uint flags, out uint numPathArrayElements, out uint numModeInfoArrayElements); [DllImport("user32.dll")] public static extern long QueryDisplayConfig(uint flags, ref uint numPathArrayElements, DISPLAYCONFIG_PATH_INFO[] pathArray, ref uint numModeInfoArrayElements, DISPLAYCONFIG_MODE_INFO[] modeInfoArray, IntPtr currentTopologyId); [DllImport("user32.dll")] public static extern long SetDisplayConfig(uint numPathArrayElements, DISPLAYCONFIG_PATH_INFO[] pathArray, uint numModeInfoArrayElements, DISPLAYCONFIG_MODE_INFO[] modeInfoArray, uint flags); } "@ # 替换为你的两个显示器设备ID $mainMonitorId = "<主显示器DeviceID>" $kamvasId = "<Kamvas DeviceID>" # 获取当前显示配置 $numPaths = 0 $numModes = 0 [DisplayConfig]::GetDisplayConfigBufferSizes(0x00000001, [ref]$numPaths, [ref]$numModes) | Out-Null $paths = New-Object DisplayConfig+DISPLAYCONFIG_PATH_INFO[] $numPaths $modes = New-Object DisplayConfig+DISPLAYCONFIG_MODE_INFO[] $numModes [DisplayConfig]::QueryDisplayConfig(0x00000001, [ref]$numPaths, $paths, [ref]$numModes, $modes, [IntPtr]::Zero) | Out-Null # 修改Kamvas位置为垂直堆叠(主显示器下方) foreach ($mode in $modes) { if ($mode.infoType -eq [DisplayConfig+DISPLAYCONFIG_MODE_INFO_TYPE]::DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) { $deviceId = "DISPLAY\$($mode.adapterIdLow.ToString('X8'))\$($mode.id.ToString('X8'))" if ($deviceId -eq $kamvasId) { # 主显示器高度1080,Kamvas Y坐标设为1080 $mode.sourceMode.position.y = 1080 $mode.sourceMode.position.x = 0 } elseif ($deviceId -eq $mainMonitorId) { $mode.sourceMode.position.x = 0 $mode.sourceMode.position.y = 0 } } } # 应用新配置 [DisplayConfig]::SetDisplayConfig($numPaths, $paths, $numModes, $modes, 0x00000001) | Out-Null
脚本2:水平排列布局(Kamvas在主显示器右侧)
新建文件HorizontalLayout.ps1,内容与垂直脚本基本一致,仅修改Kamvas的位置参数:
# ... 省略前面的Add-Type部分(与垂直脚本完全相同) ... # 修改Kamvas位置为水平排列(主显示器右侧) foreach ($mode in $modes) { if ($mode.infoType -eq [DisplayConfig+DISPLAYCONFIG_MODE_INFO_TYPE]::DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE) { $deviceId = "DISPLAY\$($mode.adapterIdLow.ToString('X8'))\$($mode.id.ToString('X8'))" if ($deviceId -eq $kamvasId) { # 主显示器宽度1920,Kamvas X坐标设为1920 $mode.sourceMode.position.x = 1920 $mode.sourceMode.position.y = 0 } elseif ($deviceId -eq $mainMonitorId) { $mode.sourceMode.position.x = 0 $mode.sourceMode.position.y = 0 } } } # 应用新配置 [DisplayConfig]::SetDisplayConfig($numPaths, $paths, $numModes, $modes, 0x00000001) | Out-Null
步骤3:绑定快捷键
- 右键点击每个
.ps1文件,选择创建快捷方式。 - 右键点击快捷方式,选择属性。
- 在快捷方式选项卡的目标字段中,在脚本路径前添加
powershell.exe -ExecutionPolicy Bypass -File,示例:powershell.exe -ExecutionPolicy Bypass -File "C:\Scripts\VerticalStack.ps1" - 在快捷键字段设置组合键(比如
Ctrl+Alt+V对应垂直堆叠,Ctrl+Alt+H对应水平排列)。 - 点击确定保存设置。
注意事项
- 若脚本报错,检查设备ID是否正确,以及屏幕分辨率是否与脚本中
1920/1080的坐标匹配(如果你的1920p是垂直分辨率,需调整对应坐标值)。 - 首次运行可能弹出PowerShell执行策略提示,选择允许即可。
内容的提问来源于stack exchange,提问作者Ian Mendes
相关产品推荐
相关产品推荐

