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

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

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

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

  2. используйте API EnumDisplayDevices и копайте, чтобы получить устройство ИДЕНТИФИКАТОР.

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

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

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

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

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

5 ответов


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

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&4F61016&0&UID257 |              |                  | array[0..0] |             |               | Dell Inc.    | Dell 2007FP (Digital) | DISPLAY\DELA021&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&4F61016&0&UID256 |              |                  | array[0..0] |             |               | Dell Inc.    | Dell ST2320L_Digital  | DISPLAY\DELF023&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 с моникерами активации), которые будут выполняться в контексте пользователя.