简体   繁体   中英

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 (ie 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?

  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 HDC s 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 HDC s 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.

    For example:

     typedef std::vector<HDC> hdc_vector; BOOL CALLBACK DisplayMonitorCallback(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { MONITORINFOEX mi = {0}; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMonitor, &mi)) { HDC dc = CreateDC(NULL, mi.szDevice, NULL, NULL); if (dc) reinterpret_cast<hdc_vector*>(dwData)->push_back(dc); } ... return TRUE; } 

     hdc_vector dcs; EnumDisplayMonitors(dcMain, nullptr, DisplayMonitorCallback, reinterpret_cast<LPARAM>(&dcs)); ... for (HDC dc : dcs) { ... } ... for (HDC dc : dcs) DeleteDC(dc); 

    Since you are clearly using C++11, I would suggest using std::unique_ptr for the memory management of the HDC s so you don't have to call DeleteDC() manually. And I would use a lambda for the callback, and change the std::vector to a std::map (so you can lookup the HDC of any specific monitor when needed):

     typedef std::unique_ptr<std::remove_pointer<HDC>::type, decltype(::DeleteDC)> device_hdc; typedef std::map<HMONITOR, device_hdc> device_hdc_map; device_hdc_map dcs; EnumDisplayMonitors(dcMain, nullptr, [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL { MONITORINFOEX mi = {0}; mi.cbSize = sizeof(mi); if (GetMonitorInfo(hMonitor, &mi)) { HDC dc = CreateDC(NULL, mi.szDevice, NULL, NULL); if (dc) (*reinterpret_cast<device_hdc_map*>(dwData))[hMonitor] = device_hdc(dc, &::DeleteDC); } ... return TRUE; }, reinterpret_cast<LPARAM>(&dcs) ); ... for (device_hdc_map::value_type &dc : dcs) { // use dc.second.get() (the actual HDC) as needed ... } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM