【问题标题】:Enumerating monitors on a computer枚举计算机上的监视器
【发布时间】:2013-08-04 01:28:49
【问题描述】:

我找到了 7 种不同的方法来枚举连接到计算机的监视器。但是所有解决方案都会给出不同的结果(监视器的数量和每个监视器上的信息)。

这些解决方案是:

  1. 使用著名的EnumDisplayDevices

  2. 使用EnumDisplayMonitors

  3. 使用Windows Management Instrumentation (WMI):
    使用以下查询:root\\WMI 命名空间中的SELECT * FROM WmiMonitorID

  4. 再次使用WMI:
    使用新查询:SELECT * FROM Win32_DesktopMonitor 位于 root\\CIMV2 命名空间中。

  5. 使用Setup API:
    首先调用SetupDiGetClassDevs检索设备信息集,然后使用SetupDiEnumDeviceInfo进行迭代

  6. 使用DirectX Graphics Infrastructure (DXGI)
    首先是IDXGIFactory::EnumAdapters,然后是IDXGIAdapter::EnumOutput

  7. 使用Connecting and Configuring Displays (CCD) APIs
    QueryDisplayConfig(QDC_ALL_PATHS, &numPathArrayElements, pathInfoArray, &numModeInfoArrayElements, modeInfoArray, nullptr);

我试图通过 MSDN 参考准确理解所有这些方法之间的区别,但徒劳无功。

观察

据我观察:

  • WmiMonitorIDSetup API 查询返回已连接(不一定是活动)显示器的列表。
  • Win32_DesktopMonitor WMI 查询返回错误(至少是意外)结果(即使在非活动状态下也仅枚举了 1 个监视器,而桌面位于另一个监视器上)。
  • EnumDisplayDevices 返回活动设备列表(除非只有 1 台显示器处于活动状态且连接了其他显示器)
  • EnumDisplayMonitorsDXGI 查询返回活动监视器列表。
  • CCD 似乎是最可靠的方法(提供目标和源之间的所有可能路径)。

问题

使用这些方法(连接显示器列表、已安装显示器列表、活动显示器列表)时,我真正期望的结果是什么?如果我使用镜像显示器或扩展显示器怎么办?如果电脑有多个显卡却没有多个输出怎么办?

Bonus:一些方法(DXGIEnumDisplayDevicesCCD)使用一种带有Adapter的层次结构- 监视器。但没有给出适配器和监视器之间的相同链接。那么,DXGI 的适配器的定义是什么? CCD?对于 EnumDisplayDevices

【问题讨论】:

  • 我想这里的诀窍是知道它们是否都调用相同的最低级别的 API 并使用它
  • 我想正确的做法是问一个更精确的问题,例如“How to enumerate X when doing Y ?”,其中 X 是监视器、物理设备、逻辑设备等。 Y是你的目标。手头有你的目标肯定会让你过滤掉一些可能性。正如您(深入且非常准确的)研究表明的那样,事情并不像您之前想象的那么简单,使用“监视器”和“计算机”等词没有可能的答案。
  • @Cedric Bignon 我不使用 Windows,但你为什么不编写一些使用所有这些方法的测试代码,可能在单独的文件中,然后反汇编二进制文件以查看如果他们进行相同的系统调用?
  • 我第二个@tibo。你的问题太开放了。此外,从一个版本的 Windows 到另一个版本也存在差异(某些 DXGI 仅适用于 Windows 8 等)。一个有用的观察结果:.NET 框架(可以被视为 Windows 上的抽象层)定义了完全基于 EnumDisplayMonitors/GetMonitorInfo 的 Screen 类 (System.Windows.Forms.Screen)(这个告诉设备名称)。
  • 还有一个:GetSystemMetrics(SM_CMONITORS) 只计算可见的显示监视器。这与 EnumDisplayMonitors 不同,后者枚举与镜像驱动程序关联的可见显示监视器和不可见伪监视器。不可见的伪监视器与用于镜像应用程序绘图以进行远程处理或其他目的的伪设备相关联。

标签: c++ windows gdi dxgi


【解决方案1】:

我不知道所有这些 API,但我确实记得其中一些(糟糕的记忆),所以这是我在 MSDN 中四处寻找和玩 wbemtest 时能记住和发现的,我很惊讶我什至记得。我知道这个答案可能不是您所希望的ALL

对于下面的插图(所有这些插图都在我的戴尔 Latitude 笔记本电脑上,我在上面给你打字,逻辑上我有两台显示器通过扩展坞连接到它)。但是,笔记本电脑已关闭,因此笔记本电脑屏幕不可见。

如果我进入显示属性,我只能看到一个屏幕。

连接到 CIMv2

select * from Win32_DesktopMonitor;

返回两个实例。

DesktopMonitor1 是外部显示器 (GenericPNPDisplay),DesktopMonitor1 是默认显示器(屏幕)。

连接到 root\WMI

select * from WMIMonitorID;

只给我一个实例,那个实例是外接显示器(我知道这是因为制造商名称是 HP)。 (HWP26CE 是 HP w2408 的标识符,参见here

然后,显示适配器和显示器之间存在差异。 EnumDisplayDevices 显示适配器,EnumDisplayMonitors 显示监视器。前者主要是仅枚举适配器,但后者允许您提供一个剪切矩形并确定该剪切矩形恰好落在哪些监视器上。当您有多个活动监视器并且有人决定做一些导致跨越多个监视器的平局时,这将变得很有用。您可以指定对EnumDisplayMonitors 的回调,并且将使用一些参数调用该回调(如果我没记错的话,其中一个参数是位于指定监视器上的指定剪切矩形的子集)。

我依稀记得SetupDiEnumDeviceInfo,我认为它为您提供了每个接口的HDEVINFO,因此(我相信)它只会在我的配置中为您提供一个条目,因为我只有一个适配器。然后你必须做点什么才能得到SP_DEVINFO_DATA

我从未使用过 DirectX 和其他 API,因此我将闭嘴谈论这两个。希望其他人能说出这两个问题,您可能会得到一个完整的答案!

【讨论】:

    【解决方案2】:

    如果与您的情况相关,在使用 Qt 5.x 时,您可以使用 QGuiApplication::screens() 方法 (http://qt-project.org/doc/qt-5.1/qtgui/qguiapplication.html#screens) 枚举所有显示。

    或者,如果不相关,您可以随时查看他们的源代码,了解他们如何枚举显示器并获取所有相关属性(包括镜像、扩展桌面等)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多