Bemipefe
Bemipefe

Reputation: 1537

Can't get display resolution for all devices using EnumDisplaySettings

I need to get the current display resolution of all monitors attached to the computer.

I can succesfully enum the display devices using the EnumDisplayDevicesA API but for an unknown reason I can't get the current display resolution for the device \\.\DISPLAY2 using the EnumDisplayDevicesA API. Here is the code:

displays = 0;
result = 1;

for (index = 0; result != 0; index++)
{
    devices[index].cb = sizeof(DISPLAY_DEVICEA);
    result = EnumDisplayDevicesA(NULL, index, &(devices[displays] ), 0);

    if (result == 0)
    {
            break;
    }

    settings[index].dmSize = sizeof(DEVMODEA);
    mode = 0;

    // Collect the settings
    while(EnumDisplaySettingsA(devices[index].DeviceName, mode, (DEVMODEA *)&(settings[displays] ) ) )
    {
        mode++;
    };

    // Read the current settings
    result = EnumDisplaySettingsA(devices[index].DeviceName, ENUM_CURRENT_SETTINGS, (DEVMODEA *)&(settings[displays] ) );

    if (result != 0)
    {
        displays++;
    }
    else 
    {
        result = GetLastError();
        printf("Error while readind display settings %d\n", result);

        //Skip this device
        result = 1;
    }
}

Note that this code works flawlessly on some computers while on computers with multiple video cards it fails. For examle it fails on a laptop with an Intel HD Graphicw 630 + Nvidia Quadro M1200 where the active displays are the main laptop display and an exteral display attached through either the HDMI or DP port). More precisely on these computers I can get the correct resolution for the first display device (\\.\DISPLAY1) but on the second display device (\\.\DISPLAY2) the returned width and height is 0.

The EnumDisplaySettingsA API just return 0 as well as the GetLastError API.

What does devices represent ? Are they a monitor attached to a single video card or are they just monitors attached to whatever video card ?

NOTE: Using EnumDisplayMonitors I can get the resolution of all monitors while EnumDisplaySettingsA fails.

Upvotes: 5

Views: 3684

Answers (3)

Detlf
Detlf

Reputation: 119

Could it be that more than two devices can be obtained by iterating over EnumDisplayDevices?
For me there were like 7 \.\DISPLAYX entries, even though I only have the Laptop Display and two external Monitors.
And when calling EnumDisplaySettings with a not connected Display, the results are like in your case 0. For me DISPLAY1, DISPLAY4 and DISPLAY5 were the ones I needed to use.

Upvotes: 3

Bemipefe
Bemipefe

Reputation: 1537

The problem is not related to the windows API nor related to the casting. The casting is not needed so I removed it. This is caused by a wrong usage of the variables in the code. As I said I only want the current resolution of each monitor and on every index of both devices and settings structures I only want data related to active displays. This is ensured by the usage of the displays variable which is only incremented when the device has readable settings. Conversely the index variable should be incremented to each loop until the EnumEnumDisplayDevicesA fails.

Unfortunately I used index also for initializing the structure and passing the device name to EnumDisplaySettingsA. My bad. So I was filling the devices structure using the devices[displays] statement while the structure passed to EnumDisplaySettingsA was devices[index]. In other words the incorrect name was passed to the API ant this is the reason while it was failing. On some computers it was working because the active monitor were the first but as soon as the DISPLAY1 or DISPALY2 was not connected the application would behave incorrectly.

This is the correct code:

displays = 0;
result = 1;

for (index = 0; result != 0; index++)
{
    devices[displays].cb = sizeof(DISPLAY_DEVICEA);
    result = EnumDisplayDevicesA(NULL, index, &(devices[displays] ), 0);

    if (result == 0)
    {
            break;
    }

    settings[displays].dmSize = sizeof(DEVMODEA);
    mode = 0;

    // Cache the settings
    EnumDisplaySettingsA(devices[displays].DeviceName, 0, &(settings[displays] ) ) 

    // Read the current settings
    result = EnumDisplaySettingsA(devices[displays].DeviceName, ENUM_CURRENT_SETTINGS, &(settings[displays] ) );

    if (result != 0)
    {
        displays++;
    }
    else 
    {
        result = GetLastError();
        printf("Error while readind display settings %d\n", result);

        //Skip this device
        result = 1;
    }

Upvotes: 3

Barmak Shemirani
Barmak Shemirani

Reputation: 31599

(DEVMODEA*)&(settings[displays])

The fact that you are using a cast suggests settings is defined incorrectly, and cast is used to hide the problem. If you have declared, for example, DEVMODEA *settings = malloc(count * sizeof(DEVMODEA)) then casting should not be necessary.

You should simply use Unicode functions as recommended by Microsoft. If you need to print ANSI, then use WideCharToMultiByte to convert Unicode to ANSI.

Moreover, you are overwriting settings[index] in the code below:

while(EnumDisplaySettingsA(devices[index].DeviceName, mode, (DEVMODEA *)&(settings[displays] ) ) )
    mode++;
result = EnumDisplaySettingsA(devices[index].DeviceName, ENUM_CURRENT_SETTINGS, (DEVMODEA *)&(settings[displays] ) );

Note that the second call to EnumDisplaySettingsA uses ENUM_CURRENT_SETTINGS, this call will never fail and may cause problems in your loop. It will also overwrite the previous value for settings[displays]

Try the code below to see if there is a difference.

int main()
{
    DISPLAY_DEVICE device = { 0 };
    device.cb = sizeof(DISPLAY_DEVICE);
    for(int index = 0;; index++)
    {
        if(!EnumDisplayDevices(NULL, index, &device, EDD_GET_DEVICE_INTERFACE_NAME))
            break;

#ifdef UNICODE
        wprintf(L"%s\n", device.DeviceName);
#else
        printf("%s\n", device.DeviceName);
#endif

        DEVMODE devmode = { 0 };
        devmode.dmSize = sizeof(DEVMODE);
        for(int modes = 0;; modes++)
        {
            if(!EnumDisplaySettings(device.DeviceName, modes, &devmode))
                break;
            printf("%d %d %d\n", 
                devmode.dmPelsWidth, 
                devmode.dmPelsHeight,
                devmode.dmDisplayFrequency);
        }
    }

    return 0;
}

Or read current resolution for each monitor

int main()
{
    int count = 0;
    DISPLAY_DEVICE temp = { 0 };
    temp.cb = sizeof(DISPLAY_DEVICE);
    while(EnumDisplayDevices(NULL, count, &temp, EDD_GET_DEVICE_INTERFACE_NAME))
        count++;
    DEVMODE *settings = malloc(count * sizeof(DEVMODE));
    DISPLAY_DEVICE *devices = malloc(count * sizeof(DISPLAY_DEVICE));

    for (int index = 0; index < count; index++)
    {
        memset(&devices[index], 0, sizeof(DISPLAY_DEVICE));
        memset(&settings[index], 0, sizeof(DEVMODE));
        devices[index].cb = sizeof(DISPLAY_DEVICE);
        settings[index].dmSize = sizeof(DEVMODE);
        if(!EnumDisplayDevices(NULL, index, &devices[index], EDD_GET_DEVICE_INTERFACE_NAME))
            break;
        if(!EnumDisplaySettings(devices[index].DeviceName, ENUM_CURRENT_SETTINGS, &settings[index]))
            break;
    }

    for(int index = 0; index < count; index++)
    {
#ifdef UNICODE
        wprintf(L"%s ", devices[index].DeviceName);
#else
        printf("%s ",  devices[index].DeviceName);
#endif
        printf("%d %d %d\n", 
            settings[index].dmPelsWidth,
            settings[index].dmPelsHeight,
            settings[index].dmDisplayFrequency);
    }
    return 0;
}

Upvotes: 3

Related Questions