EnumDisplayDevices vs WMI Win32_DesktopMonitor, how to detect active monitors?

19,342

Solution 1

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

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

Solution 2

I've just discovered you can query Win32_PnPEntity for service="monitor", and it will return all monitors.

Results on my machine:

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

Solution 3

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.

Solution 4

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.

Share:
19,342

Related videos on Youtube

Sam Saffron
Author by

Sam Saffron

Co-founder: http://www.discourse.org email: [email protected] blog: http://samsaffron.com twitter: @samsaffron Ex Stack Overflow Valued Associate #00008, creator of Stack Exchange Data Explorer, co-creator of Media Browser ( now Emby ) Other Projects: Logster : https://github.com/discourse/logster rack-mini-profiler : https://github.com/MiniProfiler/rack-mini-profiler message_bus : https://github.com/SamSaffron/message_bus memory_profiler: https://github.com/SamSaffron/memory_profiler flamegraph : https://github.com/SamSaffron/flamegraph All original source snippets I post on Stack Overflow are dedicated to the public domain. Do with them as you see fit.

Updated on April 15, 2022

Comments

  • Sam Saffron
    Sam Saffron about 2 years

    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?

  • Sam Saffron
    Sam Saffron over 15 years
    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
    Sam Saffron over 15 years
    One thing that is critical and missing from this sample is the ignoring of mirror devices and only looking at active devices. See msdn.microsoft.com/en-us/library/aa477606.aspx. Will post my current working sample.
  • Antony Woods
    Antony Woods about 12 years
    This just gives me "Generic PnP Monitor" for all monitors
  • newbie2015
    newbie2015 over 8 years
    I tried implementing your code. I am getting an error identifier DeviceID is undefined I tried searching on msdn for an appropriate include however I couldn't find any separate DeviceID parameter. Can you please tell me what header file to I include for DeviceID to be identified.