如何用Delphi实现Windows 10桌面显示器的开关控制?
Windows 10下用Delphi控制显示器开关的解决方案
旧版系统中使用的SendMessage(Application.Handle, WM_SYSCOMMAND, SC_MONITORPOWER, -1)在Windows 10失效,核心原因是系统对WM_SYSCOMMAND的接收窗口限制更严格,发送到应用窗口不再生效。以下是两种可行的实现方式:
方法一:发送消息到桌面窗口
将消息发送到系统桌面窗口(而非应用窗口),Windows 10能正确处理该命令,实现简单:
uses Windows; const SC_MONITORPOWER = $F170; // 参数说明: // -1: 开启显示器 // 1: 进入低功耗模式 // 2: 关闭显示器 procedure TurnOffMonitor; begin SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, 2); end; procedure TurnOnMonitor; begin SendMessage(GetDesktopWindow(), WM_SYSCOMMAND, SC_MONITORPOWER, -1); end;
该方法兼容性较好,大部分Win10环境下都能正常工作。如果仍失效,可尝试下面的官方API方案。
方法二:使用Display Config API(推荐Win10+)
通过系统原生的Display Config API修改显示配置,稳定性更高,适合Windows 10及以上系统,支持多显示器场景的精细控制:
首先补充声明相关API和结构体(Delphi默认Windows单元可能未完全包含):
uses Windows; type LUID = record LowPart: DWORD; HighPart: LONG; end; DISPLAYCONFIG_SOURCE_DEVICE_NAME = record header: DISPLAYCONFIG_DEVICE_INFO_HEADER; viewGdiDeviceName: array[0..CCHDEVICENAME-1] of WCHAR; end; DISPLAYCONFIG_TARGET_DEVICE_NAME = record header: DISPLAYCONFIG_DEVICE_INFO_HEADER; outputTechnology: DWORD; edidManufactureId: DWORD; edidProductCodeId: DWORD; connectorInstance: DWORD; monitorFriendlyDeviceName: array[0..CCHDEVICENAME-1] of WCHAR; monitorDevicePath: array[0..MAX_PATH-1] of WCHAR; end; DISPLAYCONFIG_PATH_INFO = record sourceInfo: DISPLAYCONFIG_SOURCE_DEVICE_NAME; targetInfo: DISPLAYCONFIG_TARGET_DEVICE_NAME; flags: DWORD; end; DISPLAYCONFIG_SOURCE_MODE = record width: UINT32; height: UINT32; pixelFormat: UINT32; position: POINTL; end; DISPLAYCONFIG_TARGET_MODE = record targetVideoSignalInfo: DISPLAYCONFIG_VIDEO_SIGNAL_INFO; end; DISPLAYCONFIG_DESKTOP_IMAGE_INFO = record pathSourceSize: SIZE; desktopImageRegion: RECTL; end; DISPLAYCONFIG_MODE_INFO = record infoType: DWORD; id: DWORD; adapterId: LUID; mode: record case Integer of 0: (sourceMode: DISPLAYCONFIG_SOURCE_MODE); 1: (targetMode: DISPLAYCONFIG_TARGET_MODE); 2: (desktopImageInfo: DISPLAYCONFIG_DESKTOP_IMAGE_INFO); end; end; const QDC_ALL_PATHS = 1; SDC_APPLY = $00000080; SDC_USE_SUPPLIED_DISPLAY_CONFIG = $00000001; DISPLAYCONFIG_PATH_ACTIVE = $00000001; function QueryDisplayConfig(flags: DWORD; var numPathArrayElements: UINT; var pathArray: PDISPLAYCONFIG_PATH_INFO; var numModeInfoArrayElements: UINT; var modeInfoArray: PDISPLAYCONFIG_MODE_INFO; var currentTopologyId: PDWORD): LONG; stdcall; external 'user32.dll'; function SetDisplayConfig(numPathArrayElements: UINT; pathArray: PDISPLAYCONFIG_PATH_INFO; numModeInfoArrayElements: UINT; modeInfoArray: PDISPLAYCONFIG_MODE_INFO; flags: DWORD): LONG; stdcall; external 'user32.dll';
然后实现开关显示器的函数:
procedure TurnOffMonitor; var numPaths, numModes: UINT; paths: array of DISPLAYCONFIG_PATH_INFO; modes: array of DISPLAYCONFIG_MODE_INFO; i: Integer; begin // 获取当前显示路径和模式的数量 QueryDisplayConfig(QDC_ALL_PATHS, numPaths, nil, numModes, nil, nil); SetLength(paths, numPaths); SetLength(modes, numModes); QueryDisplayConfig(QDC_ALL_PATHS, numPaths, @paths[0], numModes, @modes[0], nil); // 禁用所有活动显示路径 for i := 0 to numPaths - 1 do paths[i].flags := paths[i].flags and not DISPLAYCONFIG_PATH_ACTIVE; // 应用修改后的配置 SetDisplayConfig(numPaths, @paths[0], numModes, @modes[0], SDC_USE_SUPPLIED_DISPLAY_CONFIG or SDC_APPLY); end; procedure TurnOnMonitor; var numPaths, numModes: UINT; paths: array of DISPLAYCONFIG_PATH_INFO; modes: array of DISPLAYCONFIG_MODE_INFO; i: Integer; begin QueryDisplayConfig(QDC_ALL_PATHS, numPaths, nil, numModes, nil, nil); SetLength(paths, numPaths); SetLength(modes, numModes); QueryDisplayConfig(QDC_ALL_PATHS, numPaths, @paths[0], numModes, @modes[0], nil); // 启用所有显示路径 for i := 0 to numPaths - 1 do paths[i].flags := paths[i].flags or DISPLAYCONFIG_PATH_ACTIVE; SetDisplayConfig(numPaths, @paths[0], numModes, @modes[0], SDC_USE_SUPPLIED_DISPLAY_CONFIG or SDC_APPLY); end;
注意事项
- 部分环境下,Display Config API可能需要程序以管理员权限运行,可在项目设置中添加UAC清单。
- 多显示器场景下,若需单独控制某台显示器,可通过筛选特定的
targetInfo(如显示器名称、设备路径)来实现。
内容的提问来源于stack exchange,提问作者Delphi2022
相关产品推荐
相关产品推荐

