如何用WinAPI实现多显示器设置切换及代码优化指导
双显示器显示模式切换代码修正与优化建议
原代码存在的核心问题
- 副显示器设备名获取错误:循环结束后
dd指向最后一个枚举的设备,不一定是副显示器,导致ChangeDisplaySettingsEx调用无效 - 未设置副显示器的纵向模式:只修改了分辨率,没配置屏幕旋转参数,无法实现纵向显示效果
- 拓展模式配置逻辑错误:用
NULL作为设备名调用ChangeDisplaySettingsEx,且CDS_SET_PRIMARY会错误修改主显示器属性 - 缺少错误检查:所有系统API调用都没做返回值校验,出问题后无法定位原因
- 无切换功能:当前代码只能设置拓展模式,未实现「拓展↔单屏」的核心切换逻辑
修正后的完整代码
#include <windows.h> #include <iostream> // 保存显示器信息的结构体 struct DisplayInfo { WCHAR primaryDeviceName[32]; WCHAR secondaryDeviceName[32]; bool hasSecondary = false; }; // 获取主副显示器设备名 DisplayInfo GetDisplayDevices() { DisplayInfo info; DISPLAY_DEVICE dd = {0}; dd.cb = sizeof(DISPLAY_DEVICE); int displayIndex = 0; while (EnumDisplayDevices(NULL, displayIndex, &dd, 0)) { // 跳过镜像设备等非物理显示器 if (!(dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) { displayIndex++; continue; } if (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) { wcscpy_s(info.primaryDeviceName, dd.DeviceName); } else { wcscpy_s(info.secondaryDeviceName, dd.DeviceName); info.hasSecondary = true; } displayIndex++; } return info; } // 切换显示模式:拓展↔单屏 bool ToggleDisplayMode() { DisplayInfo displays = GetDisplayDevices(); if (!displays.hasSecondary) { std::cout << "未检测到副显示器" << std::endl; return false; } DEVMODE secondaryCurrentMode = {0}; secondaryCurrentMode.dmSize = sizeof(DEVMODE); // 获取副显示器当前状态 if (EnumDisplaySettings(displays.secondaryDeviceName, ENUM_CURRENT_SETTINGS, &secondaryCurrentMode) == 0) { std::cout << "获取副显示器当前设置失败,错误码:" << GetLastError() << std::endl; return false; } LONG result; if (secondaryCurrentMode.dmFields & DM_POSITION) { // 当前是拓展模式,切换为单屏(关闭副显示器) DEVMODE secondaryOffMode = {0}; secondaryOffMode.dmSize = sizeof(DEVMODE); secondaryOffMode.dmFields = DM_POSITION; secondaryOffMode.dmPosition.x = -10000; // 移到可视区域外,等同于关闭 secondaryOffMode.dmPosition.y = 0; result = ChangeDisplaySettingsEx(displays.secondaryDeviceName, &secondaryOffMode, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL); } else { // 当前是单屏模式,切换为拓展模式 // 设置主显示器参数:1920x1080 横向 DEVMODE primaryMode = {0}; primaryMode.dmSize = sizeof(DEVMODE); EnumDisplaySettings(displays.primaryDeviceName, ENUM_CURRENT_SETTINGS, &primaryMode); primaryMode.dmPelsWidth = 1920; primaryMode.dmPelsHeight = 1080; primaryMode.dmDisplayOrientation = DMDO_DEFAULT; // 横向 primaryMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYORIENTATION; result = ChangeDisplaySettingsEx(displays.primaryDeviceName, &primaryMode, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL); if (result != DISP_CHANGE_SUCCESSFUL) { std::cout << "设置主显示器失败,错误码:" << result << std::endl; return false; } // 设置副显示器参数:2160x3840 纵向,位置在主显示器右侧,顶部对齐 DEVMODE secondaryMode = {0}; secondaryMode.dmSize = sizeof(DEVMODE); EnumDisplaySettings(displays.secondaryDeviceName, ENUM_CURRENT_SETTINGS, &secondaryMode); secondaryMode.dmPelsWidth = 2160; secondaryMode.dmPelsHeight = 3840; secondaryMode.dmDisplayOrientation = DMDO_270; // 纵向(若显示不对可改为DMDO_90) secondaryMode.dmPosition.x = 1920; secondaryMode.dmPosition.y = 0; secondaryMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYORIENTATION | DM_POSITION; result = ChangeDisplaySettingsEx(displays.secondaryDeviceName, &secondaryMode, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL); } if (result != DISP_CHANGE_SUCCESSFUL) { std::cout << "切换显示模式失败,错误码:" << result << std::endl; return false; } // 批量应用所有设置 result = ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); return result == DISP_CHANGE_SUCCESSFUL; } int main() { if (ToggleDisplayMode()) { std::cout << "显示模式切换成功" << std::endl; } else { std::cout << "显示模式切换失败" << std::endl; } return 0; }
关键优化说明
- 准确识别显示器:枚举时过滤非物理显示器,精准区分主副设备并保存设备名
- 实现切换逻辑:通过读取副显示器当前位置判断模式,自动在拓展/单屏间切换
- 完善纵向设置:添加
dmDisplayOrientation参数控制屏幕旋转,确保副显示器为纵向模式(若显示方向错误,可替换DMDO_270为DMDO_90) - 全链路错误检查:所有API调用都做返回值校验,打印错误码便于调试排查
- 批量应用设置:用
CDS_NORESET暂存配置,最后统一应用,避免多次刷新屏幕 - 可扩展为可视化按钮:只需将
ToggleDisplayMode()函数绑定到Win32窗口的按钮点击事件,即可实现点击切换的功能
内容的提问来源于stack exchange,提问作者czm
相关产品推荐
相关产品推荐

