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

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

【问题讨论】:

  • MonitorFromRect、MonitorFromPoint 或 MonitorFromWindow 怎么样?它们中的任何一个都适合你吗?他们都返回HMONITOR。
  • 很遗憾没有,我需要索引

标签: c++ windows winapi c++11


【解决方案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);
    

    【讨论】:

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