EnumDisplayDevices vs WMI Win32_DesktopMonitor, ¿cómo detectar monitores activos?

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

  •  05-07-2019
  •  | 
  •  

Pregunta

Para mi proyecto actual de C ++, necesito detectar una cadena única para cada monitor que esté conectado y activo en una gran cantidad de computadoras.

La investigación ha apuntado a 2 opciones

  1. Use WMI y consulte el Win32_DesktopMonitor para todos los monitores activos. Utilice el PNPDeviceID para la identificación única de los monitores.

  2. Use la API de EnumDisplayDevices y busque la ID del dispositivo.

Estoy interesado en usar la identificación del dispositivo para la identificación única del modelo porque los monitores que usan el controlador de plug and play predeterminado informarán una cadena genérica como el nombre del monitor " monitor de plug and play predeterminado "

He estado experimentando problemas con el método WMI, parece que solo está devolviendo 1 monitor en mi máquina Vista, observando el documento, resulta que no funciona como se esperaba en dispositivos que no son WDDM.

El EnumDisplayDevices parece ser un poco problemático para ponerse en marcha cuando se ejecuta desde un servicio en segundo plano (especialmente en Vista). Si está en la sesión 0, no devolverá información.

  • ¿Alguien más ha tenido que hacer algo similar (encontrar una cadena de modelo única para todos los monitores activos conectados)?

  • ¿Qué enfoque funcionó mejor?

¿Fue útil?

Solución

Este es mi código de trabajo en curso actual para detectar la identificación del dispositivo del monitor, de manera confiable.

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

Otros consejos

Acabo de descubrir que puedes consultar Win32_PnPEntity para service = " monitor " ;, y devolverá todos los monitores.

Resultados en mi máquina:

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

Hemos estado jugando con EnumDisplayDevices para detectar si el fabricante actual de la tarjeta de video es NVIDIA. No es lo mismo, pero tal vez ayudaría. Nuestra pieza se veía así:

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

Bastante tonto, pero trabajando.

Nunca he intentado hacerlo desde un servicio, pero EnumDisplayDevices generalmente funciona bien cuando se ejecuta como usuario. Creo que los servicios se ejecutan en una sesión separada (y sin cabeza), lo que podría explicar el problema que está viendo allí.

¿Podría ejecutar un programa auxiliar desde su servicio, haciéndose pasar por una cuenta de usuario que tiene acceso a las pantallas?

El método Win32_DesktopMonitor solo devuelve 1 monitor en mi máquina Vista también. Sin embargo, el ID de PnP parece estar configurado correctamente.

He jugado rápidamente con la API de EnumDisplayDevices, y aunque parece descubrir los detalles del adaptador de forma confiable (probablemente porque la mayoría de la gente no lo dejará como "VGA estándar" por mucho tiempo), solo devuelve " Monitor Plug and Play " para los monitores conectados.

Esto hace eco de la investigación que hice en esto hace varios años (tuve que poner un poco de código para ayudar a desempolvar esos recuerdos).

Esto es de una cuenta de usuario normal. Si tiene una forma confiable de hacer que EnumDisplayDevices devuelva el ID de PnP, incluso en las sesiones de usuario normales, me interesaría. Actualmente estamos investigando si esta información está disponible para un controlador de dispositivo.

Una cosa que podrías hacer, si ejecutar el código de la sesión # 0 no es lo suficientemente confiable, es ver si puedes generar un proceso de ayuda (ya sea utilizando CreateProcessAsUser o usando COM con monikers de activación) que se ejecutará en el contexto del usuario.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top