C++ Win32:显示监视器的捕获句柄

C++ Win32:显示监视器的捕获句柄,c++,winapi,gdi,C++,Winapi,Gdi,我目前正在开发一个应用程序,它要求连接到系统的每个屏幕都有一个HDC 我目前使用的代码如下: std::vector<HDC> dcs; HDC dcMain = ::GetDC(nullptr); // <-- don't understand this ::EnumDisplayMonitors(dcMain, nullptr, MONITORENUMPROC(&DisplayMonitorCallback), LPARAM(&dcs)); 因此,我的问

我目前正在开发一个应用程序,它要求连接到系统的每个屏幕都有一个
HDC

我目前使用的代码如下:

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

::EnumDisplayMonitors(dcMain, nullptr, MONITORENUMPROC(&DisplayMonitorCallback), LPARAM(&dcs));
因此,我的问题是:

  • 对于
    dcMain
    ,我必须将其传递进来,这是获得一个的好方法吗

  • 为什么渲染在回调中工作,但在捕获
    HDC
    s并稍后对其进行迭代时不工作

  • 是的,文档中提到了这一点:

    要为每个显示器以最佳方式绘制整个虚拟屏幕,可以使用以下代码:

    hdc = GetDC(NULL);
    EnumDisplayMonitors(hdc, NULL, MyPaintScreenEnumProc, 0);
    ReleaseDC(NULL, hdc);
    
  • 正如@andlabs建议的那样,
    HDC
    s仅在回调内部有效。这是有意义的,因为必须先获得一个
    HDC
    ,然后再释放,但只有
    EnumDisplayMonitors()
    知道如何获得每个
    HDC
    ,因此只有它知道如何正确释放每个。由于没有用于释放枚举的
    HDC
    的API函数,这意味着
    HDC
    在枚举之外无效

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

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

    任何返回显示设备上下文(DC)的函数通常都会返回主监视器的DC。要获取另一个监视器的DC,请使用
    EnumDisplayMonitors
    功能或者,您可以使用
    GetMonitorInfo
    函数中的设备名称,通过
    CreateDC
    创建DC。但是,如果函数(如
    GetWindowDC
    BeginPaint
    )为跨越多个显示器的窗口获取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 = GetDC(NULL);
    EnumDisplayMonitors(hdc, NULL, MyPaintScreenEnumProc, 0);
    ReleaseDC(NULL, hdc);
    
  • 正如@andlabs建议的那样,
    HDC
    s仅在回调内部有效。这是有意义的,因为必须先获得一个
    HDC
    ,然后再释放,但只有
    EnumDisplayMonitors()
    知道如何获得每个
    HDC
    ,因此只有它知道如何正确释放每个。由于没有用于释放枚举的
    HDC
    的API函数,这意味着
    HDC
    在枚举之外无效

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

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

    任何返回显示设备上下文(DC)的函数通常都会返回主监视器的DC。要获取另一个监视器的DC,请使用
    EnumDisplayMonitors
    功能或者,您可以使用
    GetMonitorInfo
    函数中的设备名称,通过
    CreateDC
    创建DC。但是,如果函数(如
    GetWindowDC
    BeginPaint
    )为跨越多个显示器的窗口获取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;
    }
    

  • 文档中没有详细说明,但您可以假设,鉴于类似的枚举函数在其他API中的工作方式,在枚举过程中,EnumDisplayMonitors()本身会获取给定给每个回调函数的HDC,然后在回调返回时立即释放;也就是说,只有在回调函数本身期间使用该HDC可能是安全的。但是,继续这一思路将使我们进入XY问题领域,因此:您想做什么,需要单独的监视器HDC?@andlabs谢谢,我需要HDC实例化IDXGISurface1:该方法返回与IDXGISurface1关联的HDC;您使用什么来实际创建对象?文档中没有详细说明,但是您可以假设,鉴于类似的枚举函数在其他API中的工作方式,在枚举过程中,EnumDisplayMonitors()本身获取了给定给每个回调函数的HDC,然后在回调返回时立即释放;也就是说,只有在回调函数本身期间使用该HDC可能是安全的。但是,继续这一思路将使我们进入XY问题领域,因此:您想做什么,需要单独的监视器HDC?@andlabs谢谢,我需要HDC实例化IDXGISurface1:该方法返回与IDXGISurface1关联的HDC;您使用什么来实际创建对象?
    hdc_vector dcs;
    
    EnumDisplayMonitors(dcMain, nullptr, DisplayMonitorCallback, reinterpret_cast<LPARAM>(&dcs));
    
    ...
    
    for (HDC dc : dcs)
    {
        ...
    }
    
    ...
    
    for (HDC dc : dcs)
        DeleteDC(dc);
    
    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 ...
    }