You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:25:55