EnumDisplayDevices vs WMI Win32_DesktopMonitor, как обнаружить активные мониторы?

StackOverflow https://stackoverflow.com/questions/181064

  •  05-07-2019
  •  | 
  •  

Вопрос

Для моего текущего проекта на C ++ мне нужно определить уникальную строку для каждого монитора, который подключен и активен на большом количестве компьютеров.

Исследования указали на 2 варианта

  1. Используйте WMI и запросите Win32_DesktopMonitor для всех активных мониторов.Используйте PNPDeviceID для уникальной идентификации мониторов.

  2. Используйте API EnumDisplayDevices и покопайтесь, чтобы получить идентификатор устройства.

Я заинтересован в использовании идентификатора устройства для уникальной идентификации модели, потому что мониторы, использующие драйвер plug and play по умолчанию, будут сообщать общую строку в качестве имени монитора "монитор plug and play по умолчанию".

У меня возникли проблемы с методом WMI, похоже, он возвращает только 1 монитор на моем компьютере с Vista, просматривая документ, оказывается, что он работает не так, как ожидалось, на устройствах, отличных от WDDM.

EnumDisplayDevices, кажется, немного проблематично запустить, когда он запускается из фоновой службы (особенно в Vista), если он находится в сеансе 0, он не вернет никакой информации.

  • Кому-нибудь еще приходилось делать что-то подобное (находить уникальную строку модели для всех подключенных активных мониторов?)

  • Какой подход сработал лучше всего?

Это было полезно?

Решение

Это мой текущий незавершенный код для надежного определения идентификатора устройства монитора.

CString DeviceID;
DISPLAY_DEVICE dd; 
dd.cb = sizeof(dd); 
DWORD dev = 0; 
// device index 
int id = 1; 
// monitor number, as used by Display Properties > Settings

while (EnumDisplayDevices(0, dev, &dd, 0))
{
    DISPLAY_DEVICE ddMon;
    ZeroMemory(&ddMon, sizeof(ddMon));
    ddMon.cb = sizeof(ddMon);
    DWORD devMon = 0;

    while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0))
    {
        if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE && 
                     !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
        {
            DeviceID.Format (L"%s", ddMon.DeviceID);
            DeviceID = DeviceID.Mid (8, DeviceID.Find (L"\\", 9) - 8);
        }
        devMon++;

        ZeroMemory(&ddMon, sizeof(ddMon));
        ddMon.cb = sizeof(ddMon);
    }

    ZeroMemory(&dd, sizeof(dd));
    dd.cb = sizeof(dd);
    dev++; 
}

Другие советы

Я только что обнаружил, что вы можете запросить Win32_PnPEntity для service="monitor", и он вернет все мониторы.

Результаты на моем компьютере:

select * from Win32_PnPEntity where service="monitor"

Availability | Caption               | ClassGuid                              | CompatibleID | ConfigManagerErrorCode | ConfigManagerUserConfig | CreationClassName | Description           | DeviceID                           | ErrorCleared | ErrorDescription | HardwareID  | InstallDate | LastErrorCode | Manufacturer | Name                  | PNPDeviceID                        | PowerManagementCapabilities | PowerManagementSupported | Service | Status | StatusInfo | SystemCreationClassName | SystemName
             | Dell 2007FP (Digital) | {4d36e96e-e325-11ce-bfc1-08002be10318} | array[0..0]  | 0                      | False                   | Win32_PnPEntity   | Dell 2007FP (Digital) | DISPLAY\DELA021\5&4F61016&0&UID257 |              |                  | array[0..0] |             |               | Dell Inc.    | Dell 2007FP (Digital) | DISPLAY\DELA021\5&4F61016&0&UID257 |                             |                          | monitor | OK     |            | Win32_ComputerSystem    | 8HVS05J
             | Dell ST2320L_Digital  | {4d36e96e-e325-11ce-bfc1-08002be10318} | array[0..0]  | 0                      | False                   | Win32_PnPEntity   | Dell ST2320L_Digital  | DISPLAY\DELF023\5&4F61016&0&UID256 |              |                  | array[0..0] |             |               | Dell Inc.    | Dell ST2320L_Digital  | DISPLAY\DELF023\5&4F61016&0&UID256 |                             |                          | monitor | OK     |            | Win32_ComputerSystem    | 8HVS05J

Мы играли с EnumDisplayDevices, чтобы определить, является ли текущий производитель видеокарты NVIDIA.Это не одно и то же, но, возможно, это помогло бы.Наше произведение выглядело примерно так:

int disp_num = 0;
    BOOL res = TRUE;
    do {
        DISPLAY_DEVICE disp_dev_info; 
        ZeroMemory( &disp_dev_info, sizeof(DISPLAY_DEVICE) );
        disp_dev_info.cb = sizeof(DISPLAY_DEVICE);
        res = EnumDisplayDevices( 0, disp_num++, &disp_dev_info, 0x00000001 );
        if(res &&
           disp_dev_info.DeviceString[0]!=0 && disp_dev_info.DeviceString[0]=='N' &&
           disp_dev_info.DeviceString[1]!=0 && disp_dev_info.DeviceString[1]=='V' && 
           disp_dev_info.DeviceString[2]!=0 && disp_dev_info.DeviceString[2]=='I' && 
           disp_dev_info.DeviceString[3]!=0 && disp_dev_info.DeviceString[3]=='D' && 
           disp_dev_info.DeviceString[4]!=0 && disp_dev_info.DeviceString[4]=='I' && 
           disp_dev_info.DeviceString[5]!=0 && disp_dev_info.DeviceString[5]=='A'){
            isNVidia = true;
        }
        int x = 0;
    }while( res != FALSE );

Довольно глупо, но работает.

Я никогда не пробовал делать это с помощью сервиса, но EnumDisplayDevices как правило, хорошо работает при запуске от имени пользователя.Я полагаю, что службы выполняются в отдельном (и безголовом) сеансе, что может объяснить проблему, которую вы там видите.

Не могли бы вы запустить вспомогательную программу из своего сервиса, выдавая себя за учетную запись пользователя, которая имеет доступ к дисплеям?

Метод Win32_DesktopMonitor также возвращает только 1 монитор на моем компьютере с Vista.Тем не менее, идентификатор PnP, похоже, установлен правильно.

Я быстро поиграл с API EnumDisplayDevices, и хотя кажется, что он надежно определяет детали адаптера (предположительно, потому, что большинство людей не оставляют его надолго как "Стандартный VGA"), он возвращает только "Подключи и играй монитор" для подключенных мониторов.

Это перекликается с исследованием, которое я проводил по этому поводу несколько лет назад (пришлось собрать некоторый код, чтобы помочь стереть пыль с этих воспоминаний).

Это из обычной учетной записи пользователя.Если у вас есть надежный способ заставить EnumDisplayDevices возвращать идентификатор PnP даже в обычных пользовательских сеансах, мне было бы интересно - в настоящее время мы выясняем, доступна ли какая-либо из этих сведений драйверу устройства.

Одна вещь, которую вы могли бы сделать, если запуск кода из сеанса # 0 недостаточно надежен, - это посмотреть, можете ли вы создать вспомогательный процесс (либо с помощью CreateProcessAsUser, либо с помощью COM с псевдонимами активации), который будет выполняться в контексте пользователя.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top