如何在Windows中获取显示器数量?

11

我想要统计活动显示器的数量。对于Mac,我可以使用以下方法:

CGDisplayCount nDisplays;
CGGetActiveDisplayList(0,0, &nDisplays);
log.printf("Displays connected: %d",(int)nDisplays);

我该如何在Windows中实现相同的功能?我已经找到了EnumDisplayMonitors,但我不知道该如何使用它。


2
你链接的MSDN页面提到使用GetSystemMetrics(SM_CMONITORS)来计算物理显示器的数量。这对你有用吗? - IronMensan
3个回答

26

正如您所发现的那样,EnumDisplayMonitors() 可以完成这项工作,但是调用它有点棘手。文档说明如下:

EnumDisplayMonitors 函数枚举显示器(包括与镜像驱动程序相关联的不可见伪显示器),这些显示器与指定剪辑矩形和设备上下文的可见区域的交集形成的区域相交。每枚举一个显示器,EnumDisplayMonitors 调用一次应用程序定义的 MonitorEnumProc 回调函数。请注意,GetSystemMetrics (SM_CMONITORS) 仅计算显示器。

这引导我们找到了一个更容易的解决方案:GetSystemMetrics(SM_CMONITORS)。实际上,如果你有伪显示器,则这可能比EnumDisplayMonitors() 更好。


为了演示如何调用 EnumDisplayMonitors(),请尝试以下操作:

BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    int *Count = (int*)dwData;
    (*Count)++;
    return TRUE;
}

int MonitorCount()
{
    int Count = 0;
    if (EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)&Count))
        return Count;
    return -1;//signals an error
}

这对我只在32位上有效...有什么诀窍可以让它在64位上工作吗? - alap
5
64位系统上应该可以正常运行。除非您提供更多关于“仅能运行”的具体信息,否则很难为任何人提供帮助。 - David Heffernan
好的,对不起。我使用相同的代码,当我用“win32”编译它时,它可以工作,但是用x64编译就不行了。但我明白了您的意思。 - alap
3
这段代码在x64上很好用。将它粘贴到一个全新的控制台应用程序中,就可以看到。我相信你在某处遇到了问题,但我不认为问题出在这段代码上。如果你解决不了,就发布一个问题。 - David Heffernan

4

尚未经过测试,但基本上您只需要为枚举函数提供回调即可:

int numMonitors = 0;

BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
  {
  //lprcMonitor holds the rectangle that describes the monitor position and resolution)

  numMonitors++;
  return true;
  }

int main()
  {
  EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, NULL);
  }

1
以上方法的问题在于它们无法检测Clone、Internal、External拓扑中正确的监视器数量。它们只能在Extended拓扑中获取正确的数量。使用QueryDisplayConfig函数可以以任何拓扑方式获取正确的监视器数量。您需要在标志中使用QDC_ALL_PATHS,并仅计算找到的唯一监视器数。
    DISPLAYCONFIG_TOPOLOGY_ID currTopologyId = 0;
    UINT32 numPathArrayElements = 0;
    UINT32 numModeInfoArrayElements = 0;
    LONG retCode = ::GetDisplayConfigBufferSizes(flags, &numPathArrayElements, &numModeInfoArrayElements);
    auto pathArray = std::make_unique<DISPLAYCONFIG_PATH_INFO[]>(numPathArrayElements);
    auto modeInfoArray = std::make_unique<DISPLAYCONFIG_MODE_INFO[]>(numModeInfoArrayElements);
    retCode = ::QueryDisplayConfig(flags, &numPathArrayElements, pathArray.get(), &numModeInfoArrayElements, modeInfoArray.get(), &currTopologyId);

使用 DisplayConfigGetDeviceInfo 来从路径目标中获取显示器名称。
    DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
    targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
    targetName.header.size = sizeof(targetName);
    targetName.header.adapterId = pathInfo.targetInfo.adapterId;
    targetName.header.id = pathInfo.targetInfo.id;

    LONG retCode = ::DisplayConfigGetDeviceInfo(&targetName.header);

您将在以下位置获得监视器名称:

targetName.monitorDevicePath, targetName.monitorFriendlyDeviceName

它能工作吗?https://social.msdn.microsoft.com/Forums/windowsapps/en-US/668e3cf9-4e00-4b40-a6f8-c7d2fc1afd39/how-can-i-retrieve-monitor-information?forum=windowsgeneraldevelopmentissues - rogerdpack
@rogerdpack 是的,它确实有效。实际上,我在生产环境中使用了类似的代码来处理监视器配置。 - andrebroz
可能相关:https://dev59.com/j2LVa4cB1Zd3GeqP0ujG - rogerdpack
可能相关:https://stackoverflow.com/questions/10237937/looking-for-a-reliable-mapping-of-forms-screen-devicename-to-monitor-edid-info - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接