通过索引获取(真实)监视器的句柄

Posted

技术标签:

【中文标题】通过索引获取(真实)监视器的句柄【英文标题】:get the handle of a (real) monitor by its index 【发布时间】:2015-04-02 12:24:37 【问题描述】:

假设我有 3 台显示器。如何仅通过索引获取第二个的句柄? EnumDisplayMonitors() 不起作用,因为它也枚举了伪设备,并且 EnumDisplayDevices() 没有给我句柄。

【问题讨论】:

MonitorFromRectMonitorFromPointMonitorFromWindow 怎么样?它们中的任何一个都适合你吗?他们都返回HMONITOR 很遗憾没有,我需要索引 【参考方案1】:

您需要使用EnumDisplayMonitors() 而不是EnumDisplayDevices() 来访问每个监视器的HMONITOR 句柄。

但是,监视器不是由索引标识的。 GetMonitorInfo() 可以告诉你哪个监视器是“主要的”,但仅此而已。没有办法知道哪个监视器是“第二个”、“第三个”等。而且您也不能使用监视器位置来确定这一点,因为“第二个”监视器可以放置在与“主”相关的任何位置监视器,然后可以将“第三”监视器放置在与“第一”或“第二”监视器相关的任何位置。

所以你必须希望EnumDisplayMonitors()按照安装监视器的顺序进行枚举,那么你可以这样做:

struct sEnumInfo

    int iIndex;
    HMONITOR hMonitor;
;

BOOL CALLBACK GetMonitorByIndex(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)

    sEnumInfo *info = (sEnumInfo*) dwData;
    if (--info->iIndex < 0)
    
        info->hMonitor = hMonitor;
        return FALSE;
    
    return TRUE;

sEnumInfo info;
info.iIndex = 1;
info.hMonitor = NULL;

EnumDisplayMonitors(NULL, NULL, GetMonitorByIndex, (LPARAM)&info);
if (info.hMonitor != NULL)

    //...

【讨论】:

【参考方案2】:

你可以用EnumDisplayMonitors()枚举设备,用EnumDisplayDevices()检查它是否是伪监视器

当您使用 GetMonitorInfo() 遍历显示监视器时,您可以获得带有监视器设备名称的 MONITORINFOEX

然后使用EnumDisplayDevices(),您可以获得DISPLAY_DEVICE,其中包含StateFlags,如果当前监视器是伪监视器(或者如下面的情况下连接到桌面)

BOOL DispayEnumeratorProc(_In_ HMONITOR hMonitor, _In_ HDC hdcMonitor, _In_ LPRECT lprcMonitor, _In_ LPARAM dwData)

    TClass* self = (TClass*)dwData;
    if (self == nullptr)
        return FALSE;

    MONITORINFOEX monitorInfo;
    ::ZeroMemory(&monitorInfo, sizeof(monitorInfo));
    monitorInfo.cbSize = sizeof(monitorInfo);

    BOOL res = ::GetMonitorInfo(hMonitor, &monitorInfo);
    if (res == FALSE)
        return TRUE;

    DISPLAY_DEVICE displayDevice;
    ::ZeroMemory(&displayDevice, sizeof(displayDevice));
    displayDevice.cb = sizeof(displayDevice);

    res = ::EnumDisplayDevices(monitorInfo.szDevice, 0, &displayDevice, 0);
    if (res == FALSE)
        return TRUE;

    if (displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
        self->RegisterDisplay(monitorInfo);

    return TRUE;


void TClass::EnumerateDisplayMonitors()

    BOOL res = ::EnumDisplayMonitors(NULL, NULL, &DispayEnumeratorProc, (LPARAM)this);
    if (res == FALSE)
        Print("Failed");

您还可以通过遍历EnumDisplayDevices() 对您的显示器进行排序

如果您将NULL 作为第一个参数传递给EnumDisplayDevices(),它将根据第二个参数返回适配器的信息。在这种情况下,您的设备将确定顺序。

您可以将DISPLAY_DEVICE 中的DeviceName 与您之前存储的MONITORINFOEX 中的szDevice 进行比较,以对您的HMONITORs 进行排序

void TClass::SortDisplayMonitors()

    DISPLAY_DEVICE displayDevice;
    ::ZeroMemory(&displayDevice, sizeof(displayDevice));
    displayDevice.cb = sizeof(displayDevice);

    std::map<std::string, DWORD> devices;
    for (DWORD iDevNum = 0; ::EnumDisplayDevices(NULL, iDevNum, &displayDevice, 0) != FALSE; ++iDevNum)
        devices.insert(displayDevice.DeviceName, iDevNum);

    auto compare = [&devices](MONITORINFOEX& l, MONITORINFOEX& r)
        
            DWORD il = -1;
            DWORD ir = -1;

            auto foundL = devices.lower_bound(l.szDevice);
            if (foundL != devices.end())
                il = foundL->second;

            auto foundR = devices.lower_bound(r.szDevice);
            if (foundR != devices.end())
                ir = foundR->second;

            return (il < ir);
        ;

    std::sort(m_monitors.begin(), m_monitors.end(), compare);

PS:你可以写 DWORD il = std::numeric_limits::max(); 安装的 DWORD il = -1; 但不要忘记在包含 Windows.h 之前定义 NOMINMAX

【讨论】:

我认为您应该添加一个解释,说明您为什么这样做。【参考方案3】:

您可以排除主监视器,这里是示例代码(样式可能会有所不同):

如果DEVMODE dmPosition x == 0 和 y == 0,则它是主监视器。

仅对于显示设备,一个 POINTL 结构表示 显示设备的位置坐标参考 桌面区域。主显示设备始终位于 坐标 (0,0)。

检查 x、y 以定义第二个或第三个。

  LONG second_x=0;
  LONG second_y=0;

  DWORD deviceNum = 0;
  DISPLAY_DEVICE displayDevice;
  DEVMODE devMode;

  memset(&displayDevice, 0, sizeof(displayDevice));
  displayDevice.cb = sizeof(DISPLAY_DEVICE);
  while(EnumDisplayDevices(NULL, deviceNum, &displayDevice, 0))
  
    EnumDisplaySettings(displayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &devMode);
    if (devMode.dmPosition.x == 0 && devMode.dmPosition.y == 0)
    
      // primary monitor
    
    else
    
      // second or third monitor
      second_x = devMode.dmPosition.x;
      second_y = devMode.dmPosition.y;
    
    ++deviceNum;
  

  m_pMainWnd->SetWindowPos(NULL,(int)second_x,(int)second_y,0,0,SWP_SHOWWINDOW | SWP_NOSIZE);

【讨论】:

以上是关于通过索引获取(真实)监视器的句柄的主要内容,如果未能解决你的问题,请参考以下文章

Win32:捕获句柄到显示监视器

通过指针在 gdb/Eclipse CDT 调试监视中显示真实对象层次结构

在android工作室中没有显示的真实设备[重复]

我是不是使用正确的方法来监视创建句柄时要执行的任务?

Windows资源监视器软件的原理

windows删除文件时,提示“操作无法完成 因为文件已在。。。”解决方案