Win32:捕获显示监视器的句柄
Win32: capture handle to Display monitor
我目前正在开发一个应用程序,该应用程序需要 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 上下文中(即在回调中)完美运行。
现在,问题是,我正在捕获那些 HDC
供以后使用。
几行之后,我将迭代我的 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
,我必须把这个传进来,这是获得一个的好方法吗?
为什么渲染在回调中工作,但当我捕获 HDC
s 并稍后迭代它们时不工作?
是的,EnumDisplayMonitors()
文档中提到了这一点:
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);
正如@andlabs 建议的那样,HDC
仅在回调内部有效。这是有道理的,因为必须获得一个HDC
然后释放,但是只有EnumDisplayMonitors()
知道如何每个HDC
被获得,所以只有它知道如何正确地释放每一个。由于没有 API 函数用于释放枚举 HDC
,这意味着 HDC
在枚举之外无效。
MSDN 告诉您如何获得给定监视器的 HDC
:
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.
例如:
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
进行 HDC
的内存管理,这样您就不必手动调用 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 ...
}
我目前正在开发一个应用程序,该应用程序需要 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 上下文中(即在回调中)完美运行。
现在,问题是,我正在捕获那些 HDC
供以后使用。
几行之后,我将迭代我的 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
,我必须把这个传进来,这是获得一个的好方法吗?为什么渲染在回调中工作,但当我捕获
HDC
s 并稍后迭代它们时不工作?
是的,
EnumDisplayMonitors()
文档中提到了这一点: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);
正如@andlabs 建议的那样,HDC
仅在回调内部有效。这是有道理的,因为必须获得一个HDC
然后释放,但是只有EnumDisplayMonitors()
知道如何每个HDC
被获得,所以只有它知道如何正确地释放每一个。由于没有 API 函数用于释放枚举HDC
,这意味着HDC
在枚举之外无效。MSDN 告诉您如何获得给定监视器的
HDC
:HMONITOR and the Device Context
Each physical display is represented by a monitor handle of type
HMONITOR
. A validHMONITOR
is guaranteed to be non-NULL. A physical display has the sameHMONITOR
as long as it is part of the desktop. When aWM_DISPLAYCHANGE
message is sent, any monitor may be removed from the desktop and thus itsHMONITOR
becomes invalid or has its settings changed. Therefore, an application should check whether allHMONITORS
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 theGetMonitorInfo
function to create a DC withCreateDC
. However, if the function, such asGetWindowDC
orBeginPaint
, gets a DC for a window that spans more than one display, the DC will also span the two displays.例如:
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
进行HDC
的内存管理,这样您就不必手动调用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 ... }