为何TDisplay.Index与Windows索引不一致?如何获取系统一致显示顺序
获取与系统设置一致的显示器索引(Delphi跨平台方案)
Delphi默认的Screen.Displays数组按屏幕虚拟坐标(从左到右、从上到下)排序,和Windows/Mac系统设置里的显示器编号顺序不一致。以下是分平台的解决方案:
Windows平台实现
Windows系统的显示器编号顺序由显示配置的活动路径决定,需通过QueryDisplayConfig API获取系统原生排序,再映射到Delphi的TMonitor对象:
示例代码
uses Winapi.Windows, Winapi.DisplayConfig, Winapi.Monitors, System.Types; function GetWindowsSystemOrderedDisplays: TArray<TMonitor>; var PathCount, ModeCount: UINT; Paths: PDISPLAYCONFIG_PATH_INFO; Modes: PDISPLAYCONFIG_MODE_INFO; i: Integer; Monitor: TMonitor; DisplayDevice: TDisplayDevice; begin Result := []; // 获取配置缓冲区大小 if GetDisplayConfigBufferSizes(QDC_ALL_PATHS, @PathCount, @ModeCount) <> ERROR_SUCCESS then Exit; GetMem(Paths, PathCount * SizeOf(DISPLAYCONFIG_PATH_INFO)); GetMem(Modes, ModeCount * SizeOf(DISPLAYCONFIG_MODE_INFO)); try if QueryDisplayConfig(QDC_ALL_PATHS, @PathCount, Paths, @ModeCount, Modes, nil) <> ERROR_SUCCESS then Exit; for i := 0 to PathCount - 1 do begin // 仅处理活动的显示器路径 if (Paths[i].flags and DISPLAYCONFIG_PATH_ACTIVE) = 0 then Continue; // 通过设备ID枚举显示器设备 FillChar(DisplayDevice, SizeOf(DisplayDevice), 0); DisplayDevice.cb := SizeOf(DisplayDevice); if EnumDisplayDevices(PChar(Paths[i].targetInfo.adapterId), Paths[i].targetInfo.id, DisplayDevice, 0) then begin // 匹配Delphi的TMonitor对象 for Monitor in Screen.Monitors do begin if Monitor.DeviceName = DisplayDevice.DeviceName then begin Result := Result + [Monitor]; Break; end; end; end; end; finally FreeMem(Paths); FreeMem(Modes); end; end;
说明
- 返回数组顺序与Windows设置中显示器编号完全一致(索引0对应系统里的「显示器1」,索引1对应「显示器2」)。
- 自动过滤非活动显示器路径,只返回当前正在使用的显示器。
MacOS平台实现
Mac系统中,Cocoa框架的NSScreen.screens方法返回的屏幕顺序就是系统偏好设置里的显示器列表顺序,通过匹配屏幕边界可映射到Delphi的TMonitor:
示例代码
uses Macapi.AppKit, Macapi.Foundation, System.Types; function GetMacSystemOrderedDisplays: TArray<TMonitor>; var NScreens: NSArray; i: Integer; NSScr: NSScreen; Monitor: TMonitor; ScreenRect: CGRect; begin Result := []; NScreens := TNSScreen.Wrap(TNSScreen.OCClass.screens); for i := 0 to NScreens.count - 1 do begin NSScr := TNSScreen.Wrap(NScreens.objectAtIndex(i)); ScreenRect := NSScr.frame; // 通过屏幕边界匹配Delphi的TMonitor for Monitor in Screen.Monitors do begin if (Monitor.Bounds.Left = Round(ScreenRect.origin.x)) and (Monitor.Bounds.Top = Round(ScreenRect.origin.y)) and (Monitor.Bounds.Width = Round(ScreenRect.size.width)) and (Monitor.Bounds.Height = Round(ScreenRect.size.height)) then begin Result := Result + [Monitor]; Break; end; end; end; end;
说明
NSScreen.screens的顺序与Mac系统偏好设置中「显示器」面板的列表顺序完全一致。- 若存在分辨率缩放,直接取整匹配即可适配常规场景。
跨平台统一调用
通过条件编译封装统一函数,自动适配不同平台:
function GetSystemOrderedDisplays: TArray<TMonitor>; begin {$IFDEF MSWINDOWS} Result := GetWindowsSystemOrderedDisplays; {$ENDIF} {$IFDEF MACOS} Result := GetMacSystemOrderedDisplays; {$ENDIF} end;
内容的提问来源于stack exchange,提问作者Ballinger
相关产品推荐
相关产品推荐

