Не удается получить разрешение экрана для всех устройств, использующих EnumDisplaySettings - PullRequest
2 голосов
/ 10 февраля 2020

Мне нужно получить текущее разрешение экрана всех мониторов, подключенных к компьютеру.

Я могу успешно перечислить устройства отображения с помощью API EnumDisplayDevicesA , но по неизвестной причине не могу получить текущее разрешение экрана для устройства \\. \ DISPLAY2 используя API EnumDisplayDevicesA . Вот код:

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;
    }
}

Обратите внимание, что этот код работает безупречно на некоторых компьютерах, в то время как на компьютерах с несколькими видеокартами происходит сбой. Например, он не работает на ноутбуке с Intel HD Graphicw 630 + Nvidia Quadro M1200, где активными дисплеями являются основной дисплей ноутбука и внешний дисплей, подключенный через порт HDMI или DP). Точнее, на этих компьютерах я могу получить правильное разрешение для первого устройства отображения (\\. \ DISPLAY1), но на втором устройстве отображения (\\. \ DISPLAY2) возвращаемая ширина и высота равна 0.

API EnumDisplaySettingsA просто возвращает 0, а также API GetLastError .

Что представляют собой устройства? Являются ли они мониторами, подключенными к одной видеокарте, или они просто подключены к какой-либо видеокарте?

ПРИМЕЧАНИЕ: Использование EnumDisplayMonitors Я могу получить разрешение всех мониторов, пока EnumDisplaySettingsA не удается.

Ответы [ 3 ]

2 голосов
/ 14 февраля 2020
(DEVMODEA*)&(settings[displays])

Тот факт, что вы используете приведение, предполагает, что settings определен неправильно, и приведение используется, чтобы скрыть проблему. Например, если вы объявили DEVMODEA *settings = malloc(count * sizeof(DEVMODEA)), приведение не требуется.

Вы должны просто использовать функции Unicode в соответствии с рекомендациями Microsoft. Если вам нужно распечатать ANSI, используйте WideCharToMultiByte для преобразования Unicode в ANSI.

Кроме того, вы перезаписываете settings[index] в приведенном ниже коде:

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

Обратите внимание, что при втором вызове EnumDisplaySettingsA используется ENUM_CURRENT_SETTINGS, этот вызов никогда не будет неудачным и может вызвать проблемы в вашем l oop. Также будет перезаписано предыдущее значение для settings[displays]

Попробуйте приведенный ниже код, чтобы увидеть, есть ли разница.

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;
}

Или считайте текущее разрешение для каждого монитора

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;
}
2 голосов
/ 15 февраля 2020

Проблема не связана с API windows и не связана с приведением типов. Кастинг не нужен, поэтому я его убрал. Это вызвано неправильным использованием переменных в коде. Как я уже сказал, мне нужно только текущее разрешение каждого монитора и для каждого индекса структур devices и settings, мне нужны только данные, относящиеся к активным дисплеям. Это обеспечивается использованием переменной displays, которая увеличивается только тогда, когда устройство имеет читаемые настройки. И наоборот, переменная index должна увеличиваться до каждого l oop до тех пор, пока не произойдет сбой EnumEnumDisplayDevicesA.

К сожалению, я использовал index также для инициализации структуры и передачи имени устройства в EnumDisplaySettingsA. Виноват. Поэтому я заполнял структуру устройств с помощью оператора devices[displays], а структура, переданная EnumDisplaySettingsA, была devices[index]. Другими словами, неправильное имя было передано в API, и это причина, по которой он не работал. На некоторых компьютерах он работал, потому что активный монитор был первым, но как только DISPLAY1 или DISPALY2 не были подключены, приложение будет работать неправильно.

Это правильный код:

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;
    }
2 голосов
/ 14 февраля 2020

Может ли быть так, что более двух устройств можно получить путем перебора EnumDisplayDevices?
Для меня было примерно 7 \. \ DISPLAYX записей, хотя у меня только дисплей ноутбука и два внешних монитора.
И при вызове EnumDisplaySettings с неподключенным дисплеем результаты будут такими же, как в вашем случае 0. Для меня DISPLAY1, DISPLAY4 и DISPLAY5 были те, которые мне нужно было использовать.

...