Search code examples
c++winapimultiple-monitors

How to check if the monitor is connected within Windows?


What I want to do.

I use Sunshine & Moonlight for streaming games. Because I have multiple physical monitors, I can't simply choose Duplicate desktop when streaming games.

Also the resolution of the streaming client is usually not the same as the resolution supported by the physical monitors, so I choose to use a virtual monitor for streaming. (IddSampleDriver)

So write your own simple tool to disconnect all monitors except the specified one when streaming. At the end of the stream, restore the monitor connection state to what it was before.

My attempts

enter image description here

I set the third monitor to disconnected in the system's display settings. The status of the monitors was then checked using the code below.

struct MonitorInfo {
    std::string name;
    std::string deviceString;
    int width;
    int height;
    int refreshRate;
    DWORD active;
};

std::vector<MonitorInfo> GetMonitorInfo() {
    std::vector<MonitorInfo> monitors;
    DISPLAY_DEVICE displayDevice;
    displayDevice.cb = sizeof(DISPLAY_DEVICE);
    int deviceIndex = 0;

    while (EnumDisplayDevices(NULL, deviceIndex, &displayDevice, 0)) {
        MonitorInfo info;
        info.name = displayDevice.DeviceName;
        info.deviceString = displayDevice.DeviceString;

        DEVMODE devMode;
        devMode.dmSize = sizeof(DEVMODE);
        devMode.dmDriverExtra = 0;
        if (EnumDisplaySettingsEx(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode, 0)) {
            info.width = devMode.dmPelsWidth;
            info.height = devMode.dmPelsHeight;
            info.refreshRate = devMode.dmDisplayFrequency;
            info.active = displayDevice.StateFlags; // <- all monitors return the same value `DISPLAY_DEVICE_ACTIVE`
        }

        monitors.push_back(info);
        deviceIndex++;
    }

    return monitors;
}

int main(int argc, char* argv[]) {
    std::vector<MonitorInfo> monitors = GetMonitorInfo();
    for (const auto& monitor : monitors) {
        std::cout << "Monitor Name: " << monitor.name << std::endl;
        std::cout << "Monitor String: " << monitor.deviceString << std::endl;
        std::cout << "Resolution: " << monitor.width << "x" << monitor.height << std::endl;
        std::cout << "Refresh Rate: " << monitor.refreshRate << " Hz" << std::endl;
        std::cout << "Active: " << (monitor.active ) << std::endl;
        std::cout << std::endl;
    }
}

Running the code above gives the same value(DISPLAY_DEVICE.StateFlags) for all monitors. And the resolution of the monitor is also before disconnection, not 0

My question

How can I tell if a monitor is currently disconnected, as set up in my screencap?


Solution

  • The MonitorInfo info variable address in memory is (probably, depends on compiler) reused at each iteration, it's constructed at the beginning of the loop and destructed at the end of the loop.

    But you don't declare a constructor, so width, height, refreshRate and active values are not reset and keep the first monitors values. Depending on your compiler or IDE settings, you may get a warning or a runtime check, or nothing special.

    So just declare the structure like this for example:

    struct MonitorInfo {
        std::wstring name;
        std::wstring deviceString;
        int width;
        int height;
        int refreshRate;
        DWORD active;
    
        MonitorInfo() :active(0), width(0), height(0), refreshRate(0)
        {
        }
    };