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

Posted

技术标签:

【中文标题】Win32:捕获句柄到显示监视器【英文标题】:Win32: capture handle to Display monitor 【发布时间】:2016-09-01 16:08:01 【问题描述】:

我目前正在开发一个应用程序,该应用程序需要为连接到系统的每个屏幕提供HDC

我目前正在使用这样的代码:

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

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

我的回调如下:

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;

请注意,我目前正在每个屏幕上渲染一个绿色画笔。这在 THIS 上下文中(即在回调中)非常有效。

现在,问题是,我正在捕获那些 HDCs 以​​供以后使用。

几行之后,我将遍历我的 dcs 向量:

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);

所以,我的问题是:

    对于dcMain,我必须把这个传进去,这是获得一个的好方法吗?

    为什么渲染在回调中起作用,但当我捕获HDCs 并稍后对其进行迭代时不起作用?

【问题讨论】:

文档没有详细说明,但是您可以假设,考虑到类似的枚举函数在其他 API 中的工作方式,赋予每个回调函数的 HDC 是由 EnumDisplayMonitors() 本身在枚举过程中获取的然后在回调返回时立即释放;也就是说,只有在回调函数本身期间使用该 HDC 才可能是安全的。但是继续这种思路将把我们带入 XY 问题领域,所以:你想做什么需要单独的监视器 HDC? @andlabs 谢谢,我需要 HDC 来实例化 IDXGISurface1:msdn.microsoft.com/en-us/library/windows/desktop/… 该方法返回与 IDXGISurface1 关联的 HDC;你用什么来实际创建对象? 【参考方案1】:

    是的,EnumDisplayMonitors() 文档中提到了这一点:

    要为每个显示器优化绘制整个虚拟屏幕,您可以使用如下代码:

    hdc = GetDC(NULL);
    EnumDisplayMonitors(hdc, NULL, MyPaintScreenEnumProc, 0);
    ReleaseDC(NULL, hdc);
    

    HDCs 仅在回调内部有效,正如@andlabs 建议的那样。这是有道理的,因为必须获得一个HDC然后释放,但只有EnumDisplayMonitors()知道如何每个HDC是获得的,所以只有它知道如何 strong> 正确释放每个。由于没有用于释放枚举HDC 的API 函数,这意味着HDCs 在枚举之外无效。

    MSDN 告诉您如何获取给定监视器的HDC

    HMONITOR and the Device Context

    每个物理显示器都由HMONITOR 类型的监视器句柄表示。有效的HMONITOR 保证为非NULL。只要物理显示器是桌面的一部分,它就具有相同的HMONITOR。当发送WM_DISPLAYCHANGE 消息时,任何显示器都可能从桌面移除,因此其HMONITOR 变得无效或更改其设置。因此,应用程序在发送此消息时应检查所有HMONITORS 是否有效。

    任何返回显示设备上下文 (DC) 的函数通常都会返回主监视器的 DC。要获取另一台监视器的 DC,请使用 EnumDisplayMonitors 函数。 或者,您可以使用GetMonitorInfo 函数中的设备名称来创建带有CreateDC 的DC。但是,如果函数(例如 GetWindowDCBeginPaint)为跨越多个显示器的窗口获取 DC,则 DC 也将跨越两个显示器。

    例如:

    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);
    

    由于您显然使用的是 C++11,我建议您使用 std::unique_ptr 来管理 HDCs 的内存,这样您就不必手动调用 DeleteDC()。我会使用 lambda 进行回调,并将 std::vector 更改为 std::map(这样您就可以在需要时查找任何特定监视器的 HDC):

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

【讨论】:

以上是关于Win32:捕获句柄到显示监视器的主要内容,如果未能解决你的问题,请参考以下文章

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

使用缩放监视器捕获 Python 应用程序

通过 Win32 API 或 NVidia API 启用/禁用多个显示器?

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

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

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