Snake Snake - 3 months ago 26
C++ Question

Win32: capture handle to Display monitor

I am currently developing an application that requires an

HDC
for each of the screens connected to the system.

I am currently using code like this:

std::vector<HDC> dcs;
HDC dcMain = ::GetDC(nullptr); // <-- don't understand this

::EnumDisplayMonitors(dcMain, nullptr, MONITORENUMPROC(&DisplayMonitorCallback), LPARAM(&dcs));


My callback is as follows:

BOOL DisplayMonitorCallback(const HMONITOR monitor, const HDC hdcMonitor, const LPRECT lprcMonitor, std::vector<HDC>& dcs)
{
dcs.push_back(hdcMonitor);

// here is where it gets weird!
HBRUSH br = CreateSolidBrush(RGB(0, 255, 0));

auto rst = FillRect(hdcMonitor, lprcMonitor, br);

// Process all monitors
return TRUE;
}


Notice that I am currently rendering a green brush on each screen. This works perfectly in THIS context (i.e. within the callback).

Now, the problem is, I am capturing those
HDC
s to use at a later time.

So a couple of lines later, I'm iterating over my
dcs
vector:

for (HDC dc : dcs)
{
HBRUSH br = CreateSolidBrush(RGB(255, 255, 0));

RECT x = { 100, 100, 500, 500 };

auto rst = FillRect(dc, &x, br);

printf("%d", rst);
}


So, my questions are:


  1. for the
    dcMain
    , I have to pass this in, is this the good way to get one?

  2. why does the rendering work in the callback, but does not work when I capture the
    HDC
    s and iterate over them later?


Answer
  1. yes, and this is mentioned in the EnumDisplayMonitors() documentation:

    To paint the entire virtual screen optimally for each display monitor, you can use code like this:

    hdc = GetDC(NULL);
    EnumDisplayMonitors(hdc, NULL, MyPaintScreenEnumProc, 0);
    ReleaseDC(NULL, hdc);
    
  2. the HDCs are only valid inside of the callback, as @andlabs suggested. And this makes sense, because an HDC has to be obtained and then released, but only EnumDisplayMonitors() knows how each HDC is obtained, and so only it knows how to release each one correctly. Since there is no API function for releasing an enumerated HDC, this implies that the HDCs are not valid outside of the enumeration.

    MSDN tells you how to obtain an HDC for a given monitor:

    HMONITOR and the Device Context

    Each physical display is represented by a monitor handle of type HMONITOR. A valid HMONITOR is guaranteed to be non-NULL. A physical display has the same HMONITOR as long as it is part of the desktop. When a WM_DISPLAYCHANGE message is sent, any monitor may be removed from the desktop and thus its HMONITOR becomes invalid or has its settings changed. Therefore, an application should check whether all HMONITORS are valid when this message is sent.

    Any function that returns a display device context (DC) normally returns a DC for the primary monitor. To obtain the DC for another monitor, use the EnumDisplayMonitors function. Or, you can use the device name from the GetMonitorInfo function to create a DC with CreateDC. However, if the function, such as GetWindowDC or BeginPaint, gets a DC for a window that spans more than one display, the DC will also span the two displays.