views:

3305

answers:

4

For my current C++ project I need to detect a unique string for every monitor that is connected and active on a large number of computers.

Research has pointed to 2 options

  1. Use WMI and query the Win32_DesktopMonitor for all active monitors. Use the PNPDeviceID for unique identification of monitors.

  2. Use the EnumDisplayDevices API, and dig down to get the device ID.

I'm interested in using the device id for unique model identification because monitors using the default plug and play driver will report a generic string as the monitor name "default plug and play monitor"

I have been experiencing issues with the WMI method, it seems to be only returning 1 monitor on my Vista machine, looking at the doco it turns out it does not work as expected on non WDDM devices.

The EnumDisplayDevices seems to be a little problematic to get going when it runs from a background service (especially on Vista), If it's in session 0 it will return no info.

  • Has anyone else had to do something similar (find unique model string for all connected active monitors?)

  • What approach worked best?

A: 

I've never tried doing it from a service, but EnumDisplayDevices generally works well when run as a user. I believe that services run in a separate (and headless) session, which could explain the problem you're seeing there.

Could you run a helper program from your service, impersonating a user account that has access to the displays?

Head Geek
A: 

We've been toying with EnumDisplayDevices in order to detect if the current video card manufacturer is NVIDIA. It's not the same, but maybe it would help. Our piece looked like this:

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

Pretty dumb, but working.

akalenuk
One thing that is critical and missing from this sample is the ignoring of mirror devices and only looking at active devices. See http://msdn.microsoft.com/en-us/library/aa477606.aspx. Will post my current working sample.
Sam Saffron
A: 

The Win32_DesktopMonitor method only returns 1 monitor on my Vista machine as well. The PnP ID seems to be set correctly, though.

I've had a quick play with the EnumDisplayDevices API, and while it seems to discover the adapter details reliably (presumably because most people won't leave it as "Standard VGA" for long), it only returns "Plug and Play Monitor" for the connected monitors.

This echoes research that I did into this several years ago (had to put some code together to aid in dusting those memories off).

This is from a normal user account. If you've got a reliable way to get EnumDisplayDevices to return the PnP ID, even in normal user sessions, I'd be interested -- we're currently investigating if any of this information is available to a device driver.

One thing you could do, if running the code from session #0 isn't reliable enough, is to see if you can spawn a helper process (either using CreateProcessAsUser or using COM with activation monikers) that'll run in the user's context.

Roger Lipscombe
Yerp, CreateProcessAsUser works fine, we tested that the other day. It does introduce quite a lot of complexity but seems to be the only way to get that info reliably
Sam Saffron
+3  A: 

This is my current work-in-progress code for detecting the monitor device id, reliably.

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