c++ - EnumDisplayDevices vs WMI Win32_DesktopMonitor, ¿cómo detectar monitores activos?
windows winapi (5)
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 señalado a 2 opciones
Use WMI y consulte Win32_DesktopMonitor para todos los monitores activos. Use PNPDeviceID para la identificación única de monitores.
Use la API EnumDisplayDevices y cave para obtener la ID del dispositivo.
Estoy interesado en usar la identificación del dispositivo para una identificación de modelo única porque los monitores que usan el controlador plug and play predeterminado informarán una cadena genérica como el nombre del monitor "monitor plug and play predeterminado"
He estado teniendo problemas con el método WMI, parece que solo estoy devolviendo 1 monitor en mi máquina Vista, mirando el doco resulta que no funciona como se esperaba en dispositivos que no son WDDM.
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 (buscar una cadena de modelo única para todos los monitores activos conectados?)
¿Qué enfoque funcionó mejor?
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 intenté 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 de ayuda de su servicio, suplantando 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 tenido un juego rápido con la API EnumDisplayDevices, y aunque parece descubrir los detalles del adaptador de manera confiable (presumiblemente porque la mayoría de las personas no lo dejarán como "VGA estándar" por mucho tiempo), solo devuelve "Plug and Play Monitor" para los monitores conectados.
Esto hace eco de la investigación que hice sobre esto hace varios años (tuve que poner algunos códigos juntos para ayudar a desempolvar esos recuerdos).
Esto es de una cuenta de usuario normal. Si tiene una forma confiable de que EnumDisplayDevices devuelva la ID de PnP, incluso en las sesiones de usuario normales, me interesaría: actualmente estamos investigando si alguna parte de 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 usando CreateProcessAsUser o usando COM con monikers de activación) que se ejecutará en el contexto del usuario.
Acabo de descubrir que puede 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
Este es mi código actual de trabajo en progreso 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++;
}