Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get an HMONITOR handle from a display device name?

I want to obtain a monitor handle (HMONITOR) that can be used with the Windows multi-monitor APIs for a specific monitor attached to the system by index. For example, say I have three monitors attached to my system and forming part of my desktop; I want to get a handle to monitor 3.

I already know how to get the device name for a specific monitor by index by calling the EnumDisplayDevices function. For example:

HMONITOR MonitorFromIndex(int index /* (zero-indexed) */)
{
    DISPLAY_DEVICE dd;
    dd.cb = sizeof(dd);
    if (EnumDisplayDevices(NULL, index, &dd, 0) != FALSE)
    {
       // We found a match; make sure that it's part of the desktop.
       if ((dd.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
       {
          // Yup. Now we've got the name of the device:
          std::cout << dd.DeviceName << std::endl;

          // But how do I obtain an HMONITOR for this device?
          // ...
       }
    }
   return NULL;  // indicate failure
}

In the code above, we've found the name of the desired device (dd.DeviceName). I can use this name to create a DC for that monitor by calling CreateDC:

HDC hDC = CreateDC(dd.DeviceName, dd.DeviceName, NULL, NULL);

And I can obtain information about that monitor by calling EnumDisplaySettings:

DEVMODE dm;
dm.dmSize        = sizeof(dm);
dm.dmDriverExtra = 0;
if (EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &dm) != FALSE)
{
    std::cout << "The monitor supports " << dm.dmBitsPerPel << " bits per pixel." << std::endl;
}

Which is all great, but I want a handle to that monitor. How can I get it?

I tried to call EnumDisplayMonitors, passing a handle to the device context that I created using CreateDC, hoping to get a handle to the monitor passed to the callback function, but no such luck. The callback function was never called, and EnumDisplayMonitors returned FALSE (without setting an error code):

struct FoundMatch
{
   BOOL     found;
   HMONITOR hMonitor;
};

BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
{
   FoundMatch* pfm = reinterpret_cast<FoundMatch*>(dwData);
   pfm->found    = TRUE;
   pfm->hMonitor = hMonitor;
   return FALSE;  // stop enumerating
}

// elsewhere, after getting the device name and using it to create a DC
FoundMatch fm;
fm.found    = FALSE;
fm.hMonitor = NULL;
BOOL result = EnumDisplayMonitors(hDC, NULL, MonitorEnumProc, reinterpret_cast<LPARAM>(&fm));
like image 869
Cody Gray Avatar asked Jan 25 '16 08:01

Cody Gray


1 Answers

Sorry for such a late reply but maybe someone can find this useful.

The multi-monitor API is really minimalist to say the least. Once you've got your dd.DeviceName, it appears you have to go through EnumDisplayMonitors() enumeration until you find a match of dd.DeviceName against MONITORINFOEX.szDevice.

The MONITORINFOEX structure can be obtained by calling GetMonitorInfo().

Here is a non-compilable C++11 pseudo code:

struct DataBag
{
    HMONITOR hmon;
    TCHAR* devname;
} bag;

bag.hmon = NULL;
bag.devname = &dd.DeviceName;

BOOL bRes = EnumDisplayMonitors(
    NULL, NULL,
    [](HMONITOR hMonitor, HDC hDC, LPRECT rc, LPARAM data) -> BOOL {
        auto& bag = *reinterpret_cast<DataBag*>(data);
        MONITORINFOEX mi;
        mi.cbSize = sizeof(mi);
        if (/* match bag.devname against mi.szDevice */ && GetMonitorInfo(hMonitor, &mi))
        {
            bag.hmon = hMonitor;
            return FALSE;
        }
        return TRUE;
    },
    reinterpret_cast<LPARAM>(&bag));
if (bRes && bag.hmon)
{
    // Monitor found!
}
like image 85
polyvertex Avatar answered Nov 09 '22 08:11

polyvertex