windows EnumDisplayDevices vs WMI Win32_DesktopMonitor,如何检测活动监视器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/181064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 11:24:13  来源:igfitidea点击:

EnumDisplayDevices vs WMI Win32_DesktopMonitor, how to detect active monitors?

c++windowswinapiwmi

提问by Sam Saffron

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.

对于我当前的 C++ 项目,我需要为在大量计算机上连接并处于活动状态的每个监视器检测一个唯一的字符串。

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.

  1. 使用 WMI 并查询所有活动监视器的 Win32_DesktopMonitor。使用 PNPDeviceID 来唯一标识显示器。

  2. 使用 EnumDisplayDevices API,并深入获取设备 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"

我对使用设备 ID 进行唯一模型标识感兴趣,因为使用默认即插即用驱动程序的监视器会将通用字符串报告为监视器名称“默认即插即用监视器”

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.

我遇到了 WMI 方法的问题,它似乎只在我的 Vista 机器上返回 1 个显示器,查看 doco 后发现它在非 WDDM 设备上无法按预期工作。

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.

当 EnumDisplayDevices 从后台服务(尤其是在 Vista 上)运行时,它似乎有点问题,如果它在会话 0 中,它将不返回任何信息。

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

  • What approach worked best?

  • 有没有其他人必须做类似的事情(为所有连接的有源监视器找到唯一的模型字符串?)

  • 什么方法效果最好?

回答by Sam Saffron

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

这是我当前用于可靠地检测监视器设备 ID 的正在进行中的代码。

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

回答by rix0rrr

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

我刚刚发现您可以查询 Win32_PnPEntity 以获得 service="monitor",它会返回所有监视器。

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&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

回答by akalenuk

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:

我们一直在玩弄 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 );

Pretty dumb, but working.

相当愚蠢,但工作。

回答by Roger Lipscombe

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

Win32_DesktopMonitor 方法在我的 Vista 机器上也只返回 1 个监视器。不过,PnP ID 似乎设置正确。

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.

我已经快速使用了 EnumDisplayDevices API,虽然它似乎可以可靠地发现适配器详细信息(大概是因为大多数人不会长时间将其保留为“标准 VGA”),但它只返回“即插即用监视器”对于连接的显示器。

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.

这是来自普通用户帐户。如果您有一种可靠的方法让 EnumDisplayDevices 返回 PnP ID,即使在正常的用户会话中,我也会感兴趣——我们目前正在调查这些信息中的任何一个是否可用于设备驱动程序。

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.

如果从会话 #0 运行代码不够可靠,您可以做的一件事是查看是否可以生成将在用户上下文中运行的帮助程序进程(使用 CreateProcessAsUser 或使用带有激活名字的 COM)。

回答by Head Geek

I've never tried doing it from a service, but EnumDisplayDevicesgenerally 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.

我从未尝试过从服务中执行此操作,但EnumDisplayDevices在以用户身份运行时通常效果很好。我相信服务在单独的(和无头的)会话中运行,这可以解释您在那里看到的问题。

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

您能否从您的服务中运行一个帮助程序,模拟有权访问显示的用户帐户?